diff --git a/README.md b/README.md index 31f2b65..eae98a4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # TGrid -![TGrid logo](https://private-user-images.githubusercontent.com/13158709/329786980-42e584e9-bede-4879-b416-627060a21ef6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTU0MzY0NzIsIm5iZiI6MTcxNTQzNjE3MiwicGF0aCI6Ii8xMzE1ODcwOS8zMjk3ODY5ODAtNDJlNTg0ZTktYmVkZS00ODc5LWI0MTYtNjI3MDYwYTIxZWY2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA1MTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNTExVDE0MDI1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTU4OTEwNzEzMjc3NGExOWE2ZTM4OTdlYmViYjE1MTI2Y2VkN2FmMGM4ZDgxZWNkY2Q4MDE0Mjg0OGQ3ZGJhNmEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.TNb9oEvZpawGXAJ9heHAcp9jZMleFUK1SIK_PYZHYig) +![TGrid logo](https://tgrid.com/og.jpg) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/samchon/tgrid/blob/master/LICENSE) [![npm version](https://badge.fury.io/js/tgrid.svg)](https://www.npmjs.com/package/tgrid) @@ -9,7 +9,7 @@ TypeScript Grid Computing Framework. -TypeScript RPC (Remote Procedure Call) framework for WebSocket and Worker protocols. +TypeScript RPC (Remote Procedure Call) framework for `WebSocket` and `Worker` protocols. - `WebSocket` - `Worker` @@ -42,25 +42,30 @@ export const webSocketClientMain = async () => { await connector.connect("ws://127.0.0.1:37000/composite"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; ``` -> Execution result: -> +> [!TIP] > ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> $ npx ts-node examples/src/websocket +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` @@ -100,7 +105,8 @@ Check out the document in the [website](https://tgrid.com/docs): - [NestJS WebSocket SDK](https://tgrid.com/docs/examples/nestjs-websocket-sdk) - Learn from Projects - [Chat Application](https://tgrid.com/docs/projects/chat) - - [Grid Market](https://tgrid.com/docs/examples/market) + - [Grid Market](https://tgrid.com/docs/projects/market) + - [Mutex Server](https://tgrid.com/docs/projects/mutex) ### 🔗 Appendix - [API Documents](https://tgrid.com/api) \ No newline at end of file diff --git a/examples/package.json b/examples/package.json index ce76da6..3221f8f 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,5 +1,6 @@ { - "name": "@samchon/tgrid-example-websocket", + "private": true, + "name": "@tgrid/examples", "version": "1.0.0", "description": "", "main": "index.js", diff --git a/examples/src/nestjs/calculate.test.ts b/examples/src/nestjs/calculate.test.ts index e16959c..36175c1 100644 --- a/examples/src/nestjs/calculate.test.ts +++ b/examples/src/nestjs/calculate.test.ts @@ -17,12 +17,14 @@ export const testCalculateSdk = async () => { listener, ); - await driver.plus(10, 20); // returns 30 - await driver.multiplies(3, 4); // returns 12 - await driver.divides(5, 3); // returns 1.67 - await driver.scientific.sqrt(2); // returns 1.41 - await driver.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await driver.plus(10, 20), // returns 30 + await driver.multiplies(3, 4), // returns 12 + await driver.divides(5, 3), // returns 1.67 + await driver.scientific.sqrt(2), // returns 1.41 + await driver.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; diff --git a/examples/src/shared-worker/client.ts b/examples/src/shared-worker/client.ts index 186ae98..1a6ced1 100644 --- a/examples/src/shared-worker/client.ts +++ b/examples/src/shared-worker/client.ts @@ -21,11 +21,13 @@ export const sharedWorkerClientMain = async () => { await connector.connect("./server.js"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); console.log(stack); diff --git a/examples/src/websocket/client.ts b/examples/src/websocket/client.ts index 29a6b4a..649d93e 100644 --- a/examples/src/websocket/client.ts +++ b/examples/src/websocket/client.ts @@ -21,11 +21,14 @@ export const webSocketClientMain = async () => { await connector.connect("ws://127.0.0.1:37000/composite"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); + await connector.close(); console.log(stack); }; diff --git a/examples/src/worker/client.ts b/examples/src/worker/client.ts index 56e124d..2d7a921 100644 --- a/examples/src/worker/client.ts +++ b/examples/src/worker/client.ts @@ -24,11 +24,13 @@ export const workerClientMain = async () => { await connector.connect(`${__dirname}/server.${EXTENSION}`); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); console.log(stack); diff --git a/package.json b/package.json index 46cf6a5..b9c6169 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "Worker", "SharedWorker", "NestJS", + "nestia", "tRPC", "thread", "process" diff --git a/website/pages/docs/features/components.mdx b/website/pages/docs/features/components.mdx index e61453b..e6cc405 100644 --- a/website/pages/docs/features/components.mdx +++ b/website/pages/docs/features/components.mdx @@ -2,9 +2,24 @@ import { Tabs, Tab } from 'nextra-theme-docs' import Alert from '@mui/material/Alert'; import AlertTitle from '@mui/material/AlertTitle'; -![Sequence Diagram](/images/diagrams/sequence.png) +## 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](./websocket). + +Otherwise, let's study about the key components of the `TGrid`. + + - [`Communicator`](#communicator): network communication with remote system + - [`Provider`](#provider): object provided for remote system + - [`Listener`](#listener): interface of the remote system's `Provider` + - [`Driver`](#driver): proxy instance for calling functions of the remote system's `Provider` + + + ## Communicator +![Sequence Diagram](/images/diagrams/sequence.png) + 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](#driver)\<[Listener](#listener)\>, which can access to the remote system's [Provider](#provider), is created by this `Communicator`. @@ -104,24 +119,29 @@ export const webSocketClientMain = async () => { await connector.connect("ws://127.0.0.1:37000/composite"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await driver.plus(10, 20), // returns 30 + await driver.multiplies(3, 4), // returns 12 + await driver.divides(5, 3), // returns 1.67 + await driver.scientific.sqrt(2), // returns 1.41 + await driver.statistics.mean(1, 3, 9), // returns 4.33 + ); - console.log(...stack); + await connector.close(); + console.log(stack); }; ``` -> Execution result: -> -> ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ```bash filename="console" +> $ npx ts-node examples/src/websocket +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` Driver of Listener for RPC (Remote Procedure Call). diff --git a/website/pages/docs/features/websocket.mdx b/website/pages/docs/features/websocket.mdx index 6dbd3c5..15b83b6 100644 --- a/website/pages/docs/features/websocket.mdx +++ b/website/pages/docs/features/websocket.mdx @@ -258,7 +258,7 @@ export const webSocketServerMain = async () => { return server; }; ``` - + ```typescript filename="examples/src/providers/*.ts" showLineNumbers import { Driver } from "tgrid"; @@ -424,14 +424,16 @@ export const webSocketClientMain = async () => { await connector.connect("ws://127.0.0.1:37000/composite"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await driver.plus(10, 20), // returns 30 + await driver.multiplies(3, 4), // returns 12 + await driver.divides(5, 3), // returns 1.67 + await driver.scientific.sqrt(2), // returns 1.41 + await driver.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; ``` @@ -472,14 +474,16 @@ export interface IStatisticsCalculator { -> Execution result: -> -> ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ```bash filename="console" +> $ npx ts-node examples/src/websocket +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` @@ -507,7 +511,19 @@ For reference, the first `Header` type repersents an initial data from the remot ## NestJS Integration +If you know `NestJS`, you can develop WebSocket system much easily. + +In the server side, you can take advantages of `NestJS` controller patterns, so that you can manage WebSocket API endpoints much effectively. Also, with `NestJS` integration, it is possible to develop both HTTP and WebSocket operations at the same time. + +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 development becomes much safer. + ### Bootstrap +```bash +npx nestia setup +``` + +At first, install `Nestia` packages through `npx nestia setup` command. + ```typescript filename="examples/src/nestjs/bootstrap.ts" showLineNumbers import { WebSocketAdaptor } from "@nestia/core"; import { INestApplication } from "@nestjs/common"; @@ -523,7 +539,20 @@ export const bootstrap = async (): Promise => { }; ``` -### Controller +After that, you've to upgrade the NestJS application to support the WebSocket server. + +Just call the [`WebSocketAdaptor.upgrade()`](https://nestia.io/api/classes/WebSocketAdaptor.html#upgrade) method, then you can utilize `TGrid` in the `NestJS` server. + + +About detailed setup or more detailed informations, please refer below docs: + + - [Nestia > Guide Documents > Setup](https://nestia.io/docs/setup/) + - [Nestia > Guide Documents > WebSocketRoute](https://nestia.io/docs/core/WebSocketRoute/) + + +### NestJS Controller + + ```typescript filename="examples/src/nestjs/calculate.controller.ts" showLineNumbers import { WebSocketRoute } from "@nestia/core"; import { Controller } from "@nestjs/common"; @@ -624,6 +653,162 @@ export class CalculateController { } } ``` + + +```typescript filename="examples/src/nestjs/calculate.module.ts" showLineNumbers +import { Module } from "@nestjs/common"; + +import { CalculateController } from "./calculate.controller"; + +@Module({ + controllers: [CalculateController], +}) +export class CalculateModule {} +``` + + +```typescript filename="examples/src/providers/*.ts" showLineNumbers +import { Driver } from "tgrid"; + +import { ICalcConfig } from "../interfaces/ICalcConfig"; +import { ICalcEventListener } from "../interfaces/ICalcEventListener"; +import { ICompositeCalculator } from "../interfaces/ICompositeCalculator"; +import { IScientificCalculator } from "../interfaces/IScientificCalculator"; +import { ISimpleCalculator } from "../interfaces/ISimpleCalculator"; +import { IStatisticsCalculator } from "../interfaces/IStatisticsCalculator"; + +export abstract class CalculatorBase { + public constructor( + private readonly config: ICalcConfig, + private readonly listener: Driver, + ) {} + + protected compute(type: string, input: number[], output: number): number { + const pow: number = Math.pow(10, this.config.precision); + output = Math.round(output * pow) / pow; + this.listener.on({ type, input, output }).catch(() => {}); + return output; + } +} + +export class SimpleCalculator + extends CalculatorBase + implements ISimpleCalculator +{ + public plus(x: number, y: number): number { + return this.compute("plus", [x, y], x + y); + } + public minus(x: number, y: number): number { + return this.compute("minus", [x, y], x - y); + } + public multiplies(x: number, y: number): number { + return this.compute("multiplies", [x, y], x * y); + } + public divides(x: number, y: number): number { + return this.compute("divides", [x, y], x / y); + } +} + +export class ScientificCalculator + extends CalculatorBase + implements IScientificCalculator +{ + public pow(x: number, y: number): number { + return this.compute("pow", [x, y], Math.pow(x, y)); + } + public sqrt(x: number): number { + return this.compute("sqrt", [x], Math.sqrt(x)); + } + public log(x: number, base: number): number { + return this.compute("log", [x, base], Math.log(x) / Math.log(base)); + } +} + +export class StatisticsCalculator + extends CalculatorBase + implements IStatisticsCalculator +{ + public mean(...values: number[]): number { + const sum: number = values.reduce((x, y) => x + y); + return this.compute("mean", values, sum / values.length); + } + public stdev(...values: number[]): number { + const mean: number = values.reduce((x, y) => x + y) / values.length; + const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2)); + return this.compute("stdev", values, Math.sqrt(sum / values.length)); + } +} + +export class CompositeCalculator + extends SimpleCalculator + implements ICompositeCalculator +{ + public readonly scientific: ScientificCalculator; + public readonly statistics: StatisticsCalculator; + + public constructor( + config: ICalcConfig, + listener: Driver, + ) { + super(config, listener); + this.scientific = new ScientificCalculator(config, listener); + this.statistics = new StatisticsCalculator(config, listener); + } +} +``` + + +```typescript filename="examples/src/interfaces/*.ts" showLineNumbers +export interface ICalcConfig { + precision: number; +} +export interface ICalcEvent { + type: string; + input: number[]; + output: number; +} +export interface ICalcEventListener { + on(event: ICalcEvent): void; +} + +export interface ICompositeCalculator extends ISimpleCalculator { + scientific: IScientificCalculator; + statistics: IStatisticsCalculator; +} +export interface ISimpleCalculator { + plus(x: number, y: number): number; + minus(x: number, y: number): number; + multiplies(x: number, y: number): number; + divides(x: number, y: number): number; +} +export interface IScientificCalculator { + pow(x: number, y: number): number; + sqrt(x: number): number; + log(x: number, base: number): number; +} +export interface IStatisticsCalculator { + mean(...values: number[]): number; + stdev(...values: number[]): number; +} +``` + + + +From 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`](#websocketacceptor) type. It's because the websocket server must determine whether to [`WebSocketAcceptor.accept()`](/api/classes/WebSocketAcceptor-1.html#accept) the client's connection or [`WebSocketAcceptor.reject()`](/api/classes/WebSocketAcceptor-1.html#reject). + +Also, when declaring the [`WebSocketAcceptor`](#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](#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. + + +You can find more detailed informations about `@WebSocketRoute()`: + + - [Nestia > Guide Documents > WebSocketRoute](https://nestia.io/docs/core/WebSocketRoute/)] + ### Software Development Kit ```typescript filename="examples/src/api/functional/calculate.ts" showLineNumbers @@ -793,8 +978,25 @@ export namespace statistics { } ``` +```bash filename="console" +npx nestia sdk +``` + +When 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`](#nestjs-controller) class we've seen in the previous [NestJS Controller](#nestjs-controller) section. Client developers can utilize the automatically generated SDK functions to connect to the WebSocket server, and interact it type safely. + +Let's see how client developer utilizes the SDK library in the next section. + + +You can find more detailed informations about SDK generator: + + - [Nestia > Guide Documents > S/W Development Kit](https://nestia.io/docs/sdk/sdk/) + ### Client Application + + ```typescript filename="examples/nestjs/calculate.test.ts" showLineNumbers import api from "../api"; import { ICalcEvent } from "../interfaces/ICalcEvent"; @@ -815,28 +1017,118 @@ export const testCalculateSdk = async () => { listener, ); - await driver.plus(10, 20); // returns 30 - await driver.multiplies(3, 4); // returns 12 - await driver.divides(5, 3); // returns 1.67 - await driver.scientific.sqrt(2); // returns 1.41 - await driver.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await driver.plus(10, 20), // returns 30 + await driver.multiplies(3, 4), // returns 12 + await driver.divides(5, 3), // returns 1.67 + await driver.scientific.sqrt(2), // returns 1.41 + await driver.statistics.mean(1, 3, 9), // returns 4.33 + ); + + await connector.close(); + console.log(stack); +}; +``` + + +```typescript filename="examples/src/websocket/client.ts" showLineNumbers +import { Driver, WebSocketConnector } from "tgrid"; + +import { ICalcConfig } from "../interfaces/ICalcConfig"; +import { ICalcEvent } from "../interfaces/ICalcEvent"; +import { ICalcEventListener } from "../interfaces/ICalcEventListener"; +import { ICompositeCalculator } from "../interfaces/ICompositeCalculator"; + +export const webSocketClientMain = async () => { + const stack: ICalcEvent[] = []; + const listener: ICalcEventListener = { + on: (evt: ICalcEvent) => stack.push(evt), + }; + const connector: WebSocketConnector< + ICalcConfig, + ICalcEventListener, + ICompositeCalculator + > = new WebSocketConnector( + { precision: 2 }, // header + listener, // provider for remote server + ); + await connector.connect("ws://127.0.0.1:37000/composite"); + + const remote: Driver = connector.getDriver(); + console.log( + await driver.plus(10, 20), // returns 30 + await driver.multiplies(3, 4), // returns 12 + await driver.divides(5, 3), // returns 1.67 + await driver.scientific.sqrt(2), // returns 1.41 + await driver.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; ``` + + +```typescript filename="examples/src/interfaces/*.ts" showLineNumbers +export interface ICalcConfig { + precision: number; +} +export interface ICalcEvent { + type: string; + input: number[]; + output: number; +} +export interface ICalcEventListener { + on(event: ICalcEvent): void; +} -> Execution result: +export interface ICompositeCalculator extends ISimpleCalculator { + scientific: IScientificCalculator; + statistics: IStatisticsCalculator; +} +export interface ISimpleCalculator { + plus(x: number, y: number): number; + minus(x: number, y: number): number; + multiplies(x: number, y: number): number; + divides(x: number, y: number): number; +} +export interface IScientificCalculator { + pow(x: number, y: number): number; + sqrt(x: number): number; + log(x: number, base: number): number; +} +export interface IStatisticsCalculator { + mean(...values: number[]): number; + stdev(...values: number[]): number; +} +``` + + + +> ```bash filename="console" +> $ npx ts-node examples/src/nestjs > -> ```bash > [Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestFactory] Starting Nest application... > [Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [InstanceLoader] CalculateModule dependencies initialized +5ms > [Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [RoutesResolver] CalculateController {/calculate}: +5ms > [Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestApplication] Nest application successfully started +2ms -> -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } -> ``` \ No newline at end of file +> +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] +> ``` + +Do 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`](./components/#driver)\<`ICompositeCalculator`> instance. + +Doesn't the "SDK based development" seems much easier and safer than the previous [Natives Classes > `WebSocketConnector`](#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`](#websocketconnector) and [`Driver`](./components/#driver)\<`ICompositeCalculator`> instances, but it would be actually returned only when the websocket server [accept](/api/classes/WebSocketAcceptor-1.html#accept)s your connection. Otherwise, the websocket server [reject](/api/classes/WebSocketAcceptor-1.html#reject)s your connection, an exception would be thrown. + +Also, don't forget to [closing](/api/classes/WebSocketConnector-1.html#close) 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](#websocketserver), you can wait the remote server's closing signal through the [`WebSocketConnector.join()`](/api/classes/WebSocketConnector-1.html#join) method. \ No newline at end of file diff --git a/website/pages/docs/features/worker.mdx b/website/pages/docs/features/worker.mdx index 84d1813..a6764b7 100644 --- a/website/pages/docs/features/worker.mdx +++ b/website/pages/docs/features/worker.mdx @@ -47,14 +47,16 @@ export const workerClientMain = async () => { await connector.connect(`${__dirname}/server.${EXTENSION}`); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; ``` @@ -95,14 +97,16 @@ export interface IStatisticsCalculator { -> Execution result: -> -> ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ```bash filename="console" +> $ npx ts-node examples/src/worker +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` Worker Connector. @@ -706,14 +710,16 @@ export const sharedWorkerClientMain = async () => { await connector.connect("./server.js"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); - console.log(...stack); + console.log(stack); }; ``` @@ -754,14 +760,15 @@ export interface IStatisticsCalculator { -> Execution result: -> -> ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ```bash filename="console" +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` Shared Worker Connector. diff --git a/website/pages/docs/index.mdx b/website/pages/docs/index.mdx index 8cce9e7..678958d 100644 --- a/website/pages/docs/index.mdx +++ b/website/pages/docs/index.mdx @@ -69,23 +69,27 @@ export const webSocketClientMain = async () => { await connector.connect("ws://127.0.0.1:37000/composite"); const remote: Driver = connector.getDriver(); - await remote.plus(10, 20); // returns 30 - await remote.multiplies(3, 4); // returns 12 - await remote.divides(5, 3); // returns 1.67 - await remote.scientific.sqrt(2); // returns 1.41 - await remote.statistics.mean(1, 3, 9); // returns 4.33 + console.log( + await remote.plus(10, 20), // returns 30 + await remote.multiplies(3, 4), // returns 12 + await remote.divides(5, 3), // returns 1.67 + await remote.scientific.sqrt(2), // returns 1.41 + await remote.statistics.mean(1, 3, 9), // returns 4.33 + ); await connector.close(); console.log(stack); }; ``` -> Execution result: -> -> ```bash -> { type: 'plus', input: [ 10, 20 ], output: 30 } -> { type: 'multiplies', input: [ 3, 4 ], output: 12 } -> { type: 'divides', input: [ 5, 3 ], output: 1.67 } -> { type: 'sqrt', input: [ 2 ], output: 1.41 } -> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ```bash filename="console" +> $ npx ts-node examples/src/websocket +> 30 12 1.67 1.41 4.33 +> [ +> { type: 'plus', input: [ 10, 20 ], output: 30 }, +> { type: 'multiplies', input: [ 3, 4 ], output: 12 }, +> { type: 'divides', input: [ 5, 3 ], output: 1.67 }, +> { type: 'sqrt', input: [ 2 ], output: 1.41 }, +> { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } +> ] > ``` diff --git a/website/pages/docs/projects/_meta.json b/website/pages/docs/projects/_meta.json index 5faaf8e..9d94f64 100644 --- a/website/pages/docs/projects/_meta.json +++ b/website/pages/docs/projects/_meta.json @@ -1,4 +1,5 @@ { "chat": "Chat Application", - "market": "Grid Market" + "market": "Grid Market", + "mutex": "Remote Mutex" } diff --git a/website/pages/docs/projects/mutex.mdx b/website/pages/docs/projects/mutex.mdx new file mode 100644 index 0000000..1573787 --- /dev/null +++ b/website/pages/docs/projects/mutex.mdx @@ -0,0 +1,8 @@ +## Preparing +![Mutex Server Demo](/images/projects/mutex-server-demo.gif) + +Preparing the mutex server project documentation. + +Also, upgrading very old project to be modernized. + +https://github.com/samchon/mutex \ No newline at end of file diff --git a/website/public/images/projects/mutex-server-demo.gif b/website/public/images/projects/mutex-server-demo.gif new file mode 100644 index 0000000..2e0ec5c Binary files /dev/null and b/website/public/images/projects/mutex-server-demo.gif differ