From 96587ceadece423367f1361a2b9693f22c3ec4a5 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 13 Mar 2024 15:14:20 +0900 Subject: [PATCH] Refactoring: - Every classes and functions are exported in the root namespace - URLVariables, XML, XMLList classes are removed - Test programs are also refactored too - Documentations are later - The new version is 0.10 --- .eslintrc.cjs | 56 +- .github/workflows/build.yml | 19 +- .gitignore | 19 +- .npmignore | 4 - .vscode/extensions.json | 5 - .vscode/launch.json | 45 +- .vscode/settings.json | 8 +- build/bundle.ts | 42 -- build/clean.ts | 10 - build/dist.ts | 20 - package.json | 61 +- prettier.config.js | 14 + src/components/Communicator.ts | 706 +++++++++--------- src/components/Invoke.ts | 71 +- src/components/index.ts | 11 +- src/components/module.ts | 8 - src/index.ts | 5 - src/module.ts | 13 +- src/protocols/index.ts | 11 +- src/protocols/internal/AcceptorBase.ts | 201 +++-- src/protocols/internal/ConnectorBase.ts | 187 +++-- src/protocols/internal/IHeaderWrapper.ts | 17 +- src/protocols/internal/IServer.ts | 27 +- src/protocols/internal/once.ts | 23 +- src/protocols/module.ts | 9 - src/protocols/web/WebAcceptor.ts | 375 +++++----- src/protocols/web/WebConnector.ts | 441 ++++++----- src/protocols/web/WebError.ts | 27 +- src/protocols/web/WebServer.ts | 434 +++++------ src/protocols/web/index.ts | 13 +- .../web/internal/IWebCommunicator.ts | 37 +- .../web/internal/WebSocketPolyfill.ts | 9 +- src/protocols/web/module.ts | 9 - src/protocols/workers/SharedWorkerAcceptor.ts | 295 ++++---- .../workers/SharedWorkerConnector.ts | 402 +++++----- src/protocols/workers/SharedWorkerServer.ts | 255 ++++--- src/protocols/workers/WorkerConnector.ts | 553 +++++++------- src/protocols/workers/WorkerServer.ts | 385 +++++----- src/protocols/workers/index.ts | 13 +- src/protocols/workers/internal/FileSystem.ts | 173 +++-- src/protocols/workers/internal/IReject.ts | 9 +- .../workers/internal/IWorkerCompiler.ts | 17 +- .../workers/internal/IWorkerSystem.ts | 25 +- .../workers/internal/NodeWorkerCompiler.ts | 64 +- .../workers/internal/WebWorkerCompiler.ts | 31 +- .../internal/processes/ProcessChannel.ts | 33 +- .../internal/processes/ProcessWorker.ts | 41 +- .../workers/internal/threads/ThreadPort.ts | 26 +- .../workers/internal/threads/ThreadWorker.ts | 36 +- src/protocols/workers/module.ts | 11 - src/test/browser/index.ts | 67 -- src/test/browser/internal.ts | 7 - src/test/browser/shared-worker-client.ts | 19 - src/test/browser/shared-worker-server.ts | 16 - src/test/browser/web-client.ts | 24 - src/test/browser/web-server.ts | 18 - src/test/browser/worker-client.ts | 25 - src/test/browser/worker-server.ts | 11 - src/test/controllers/ICalculator.ts | 90 --- src/test/controllers/IChatPrinter.ts | 3 - src/test/controllers/IChatService.ts | 4 - src/test/controllers/IScript.ts | 50 -- src/test/controllers/IVector.ts | 31 - src/test/node/components/test_pseudo.ts | 55 -- src/test/node/components/test_security.ts | 49 -- src/test/node/index.ts | 70 -- .../node/protocols/web/test_web_calculator.ts | 53 -- src/test/node/protocols/web/test_web_chat.ts | 109 --- .../node/protocols/web/test_web_header.ts | 26 - src/test/node/protocols/web/test_web_mutex.ts | 89 --- .../node/protocols/web/test_web_reject.ts | 30 - .../protocols/web/test_web_server_close.ts | 18 - .../protocols/workers/internal/calculator.ts | 41 - .../protocols/workers/internal/chat-child.ts | 39 - .../node/protocols/workers/internal/join.ts | 16 - .../protocols/workers/internal/scientific.ts | 11 - .../protocols/workers/internal/statistics.ts | 11 - .../workers/test_hierarchical_workers.ts | 22 - .../node/protocols/workers/test_worker.ts | 11 - .../protocols/workers/test_worker_chat.ts | 52 -- .../protocols/workers/test_worker_compiler.ts | 41 - .../protocols/workers/test_worker_join.ts | 27 - src/test/node/test_exports.ts | 85 --- src/test/node/utils/ArrayUtil.ts | 17 - src/test/node/utils/test_url_variables.ts | 100 --- src/test/node/utils/test_xml.ts | 166 ---- src/test/providers/Calculator.ts | 61 -- src/test/providers/ChatService.ts | 55 -- src/{components => typings}/Driver.ts | 11 +- src/typings/Functional.ts | 40 +- src/typings/IJsonable.ts | 13 +- src/typings/OmitEdgeUnderscored.ts | 21 +- src/typings/Parametric.ts | 19 +- src/typings/Primitive.ts | 160 ++-- src/typings/Promisive.ts | 48 +- src/typings/RemoveNever.ts | 5 - src/typings/SpecialFields.ts | 7 +- src/typings/ValueOf.ts | 35 +- src/typings/index.ts | 12 +- src/typings/module.ts | 14 - src/utils/URLVariables.ts | 129 ---- src/utils/XML.ts | 521 ------------- src/utils/XMLList.ts | 36 - src/utils/index.ts | 9 - src/utils/internal/Dictionary.ts | 18 - src/utils/internal/NodeModule.ts | 42 +- src/utils/module.ts | 8 - test/browser/TestBundler.ts | 35 + test/browser/index.ts | 62 ++ test/browser/internal.ts | 7 + test/browser/shared-worker-client.ts | 19 + test/browser/shared-worker-server.ts | 16 + test/browser/web-client.ts | 20 + test/browser/web-server.ts | 18 + test/browser/worker-client.ts | 25 + test/browser/worker-server.ts | 11 + test/controllers/ICalculator.ts | 92 +++ test/controllers/IChatPrinter.ts | 3 + test/controllers/IChatService.ts | 4 + test/controllers/IScript.ts | 50 ++ test/controllers/IVector.ts | 30 + test/node/components/test_pseudo.ts | 48 ++ test/node/components/test_security.ts | 47 ++ test/node/index.ts | 67 ++ .../node/protocols/web/test_web_calculator.ts | 50 ++ test/node/protocols/web/test_web_chat.ts | 106 +++ test/node/protocols/web/test_web_header.ts | 25 + test/node/protocols/web/test_web_mutex.ts | 86 +++ test/node/protocols/web/test_web_reject.ts | 26 + .../protocols/web/test_web_server_close.ts | 14 + .../protocols/workers/internal/calculator.ts | 38 + .../protocols/workers/internal/chat-child.ts | 38 + test/node/protocols/workers/internal/join.ts | 16 + .../protocols/workers/internal/scientific.ts | 11 + .../protocols/workers/internal/statistics.ts | 11 + .../workers/test_hierarchical_workers.ts | 20 + test/node/protocols/workers/test_worker.ts | 11 + .../protocols/workers/test_worker_chat.ts | 52 ++ .../protocols/workers/test_worker_compiler.ts | 41 + .../protocols/workers/test_worker_join.ts | 26 + test/providers/Calculator.ts | 61 ++ test/providers/ChatService.ts | 55 ++ test/tsconfig.json | 14 + tsconfig.json | 131 ++-- 144 files changed, 4026 insertions(+), 5433 deletions(-) delete mode 100644 .npmignore delete mode 100644 .vscode/extensions.json delete mode 100644 build/bundle.ts delete mode 100644 build/clean.ts delete mode 100644 build/dist.ts create mode 100644 prettier.config.js delete mode 100644 src/components/module.ts delete mode 100644 src/protocols/module.ts delete mode 100644 src/protocols/web/module.ts delete mode 100644 src/protocols/workers/module.ts delete mode 100644 src/test/browser/index.ts delete mode 100644 src/test/browser/internal.ts delete mode 100644 src/test/browser/shared-worker-client.ts delete mode 100644 src/test/browser/shared-worker-server.ts delete mode 100644 src/test/browser/web-client.ts delete mode 100644 src/test/browser/web-server.ts delete mode 100644 src/test/browser/worker-client.ts delete mode 100644 src/test/browser/worker-server.ts delete mode 100644 src/test/controllers/ICalculator.ts delete mode 100644 src/test/controllers/IChatPrinter.ts delete mode 100644 src/test/controllers/IChatService.ts delete mode 100644 src/test/controllers/IScript.ts delete mode 100644 src/test/controllers/IVector.ts delete mode 100644 src/test/node/components/test_pseudo.ts delete mode 100644 src/test/node/components/test_security.ts delete mode 100644 src/test/node/index.ts delete mode 100644 src/test/node/protocols/web/test_web_calculator.ts delete mode 100644 src/test/node/protocols/web/test_web_chat.ts delete mode 100644 src/test/node/protocols/web/test_web_header.ts delete mode 100644 src/test/node/protocols/web/test_web_mutex.ts delete mode 100644 src/test/node/protocols/web/test_web_reject.ts delete mode 100644 src/test/node/protocols/web/test_web_server_close.ts delete mode 100644 src/test/node/protocols/workers/internal/calculator.ts delete mode 100644 src/test/node/protocols/workers/internal/chat-child.ts delete mode 100644 src/test/node/protocols/workers/internal/join.ts delete mode 100644 src/test/node/protocols/workers/internal/scientific.ts delete mode 100644 src/test/node/protocols/workers/internal/statistics.ts delete mode 100644 src/test/node/protocols/workers/test_hierarchical_workers.ts delete mode 100644 src/test/node/protocols/workers/test_worker.ts delete mode 100644 src/test/node/protocols/workers/test_worker_chat.ts delete mode 100644 src/test/node/protocols/workers/test_worker_compiler.ts delete mode 100644 src/test/node/protocols/workers/test_worker_join.ts delete mode 100644 src/test/node/test_exports.ts delete mode 100644 src/test/node/utils/ArrayUtil.ts delete mode 100644 src/test/node/utils/test_url_variables.ts delete mode 100644 src/test/node/utils/test_xml.ts delete mode 100644 src/test/providers/Calculator.ts delete mode 100644 src/test/providers/ChatService.ts rename src/{components => typings}/Driver.ts (78%) delete mode 100644 src/typings/module.ts delete mode 100644 src/utils/URLVariables.ts delete mode 100644 src/utils/XML.ts delete mode 100644 src/utils/XMLList.ts delete mode 100644 src/utils/index.ts delete mode 100644 src/utils/internal/Dictionary.ts delete mode 100644 src/utils/module.ts create mode 100644 test/browser/TestBundler.ts create mode 100644 test/browser/index.ts create mode 100644 test/browser/internal.ts create mode 100644 test/browser/shared-worker-client.ts create mode 100644 test/browser/shared-worker-server.ts create mode 100644 test/browser/web-client.ts create mode 100644 test/browser/web-server.ts create mode 100644 test/browser/worker-client.ts create mode 100644 test/browser/worker-server.ts create mode 100644 test/controllers/ICalculator.ts create mode 100644 test/controllers/IChatPrinter.ts create mode 100644 test/controllers/IChatService.ts create mode 100644 test/controllers/IScript.ts create mode 100644 test/controllers/IVector.ts create mode 100644 test/node/components/test_pseudo.ts create mode 100644 test/node/components/test_security.ts create mode 100644 test/node/index.ts create mode 100644 test/node/protocols/web/test_web_calculator.ts create mode 100644 test/node/protocols/web/test_web_chat.ts create mode 100644 test/node/protocols/web/test_web_header.ts create mode 100644 test/node/protocols/web/test_web_mutex.ts create mode 100644 test/node/protocols/web/test_web_reject.ts create mode 100644 test/node/protocols/web/test_web_server_close.ts create mode 100644 test/node/protocols/workers/internal/calculator.ts create mode 100644 test/node/protocols/workers/internal/chat-child.ts create mode 100644 test/node/protocols/workers/internal/join.ts create mode 100644 test/node/protocols/workers/internal/scientific.ts create mode 100644 test/node/protocols/workers/internal/statistics.ts create mode 100644 test/node/protocols/workers/test_hierarchical_workers.ts create mode 100644 test/node/protocols/workers/test_worker.ts create mode 100644 test/node/protocols/workers/test_worker_chat.ts create mode 100644 test/node/protocols/workers/test_worker_compiler.ts create mode 100644 test/node/protocols/workers/test_worker_join.ts create mode 100644 test/providers/Calculator.ts create mode 100644 test/providers/ChatService.ts create mode 100644 test/tsconfig.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 149401b..a7c5c75 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,32 +1,28 @@ module.exports = { - root: true, - plugins: [ - "@typescript-eslint" - ], - extends: [ - "plugin:@typescript-eslint/recommended", - ], - parser: "@typescript-eslint/parser", - parserOptions: { - project: "tsconfig.json" + root: true, + plugins: ["@typescript-eslint"], + extends: ["plugin:@typescript-eslint/recommended"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "tsconfig.json", + }, + overrides: [ + { + files: ["src/**/*.ts"], + rules: { + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/no-duplicate-imports": "error", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-floating-promises": "error", + }, }, - overrides: [ - { - files: ["src/**/*.ts"], - rules: { - "@typescript-eslint/consistent-type-definitions": "off", - "@typescript-eslint/no-duplicate-imports": "error", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-floating-promises": "error" - } - } - ] -}; \ No newline at end of file + ], +}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5b0bc9..85e8cc7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,21 +5,20 @@ jobs: NodeJS: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 16.x + node-version: 20.x - run: npm install - - run: npm run compile + - run: npm run build - run: npm run test:node Browser: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 16.x - - run: npm install --force - - run: npm run compile - - run: npm run bundle + node-version: 20.x + - run: npm install + - run: npm run build:test - run: npm run test:browser \ No newline at end of file diff --git a/.gitignore b/.gitignore index b5ee0aa..8e30fff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,8 @@ -# ====================================================== -# CUSTOM IGNORE -# ====================================================== -node_modules/ -dist/ .cache/ .parcel-cache/ +bin/ +lib/ +node_modules/ -package-lock.json - -#---- -# COMPILED FILES -#---- -*.js -*.js.map -*.d.ts -*.d.ts.map \ No newline at end of file +bundle/*.js +package-lock.json \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index f2aff2a..0000000 --- a/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -test/ -benchmark/ - -*.js.map \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 0f148de..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "recommendations": [ - "esbenp.prettier-vscode" - ] -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index fd53cfc..d7686c9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,25 +1,24 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceRoot}/test/main.js", - "cwd": "${workspaceRoot}", - - // TypeScript - "sourceMaps": true - }, - { - "type": "node", - "request": "attach", - "name": "Attach to Process", - "port": 5858, - "outFiles": [] - } - ] + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceRoot}/bin/test/main.js", + "cwd": "${workspaceRoot}", + // TypeScript + "sourceMaps": true + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Process", + "port": 5858, + "outFiles": [] + } + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ec80d6..7818719 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,10 @@ { - "editor.tabSize": 4, - "[typescript]": { + "editor.tabSize": 2, + "editor.formatOnSave": true, + "[typescript][javascript][json]": { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, } } \ No newline at end of file diff --git a/build/bundle.ts b/build/bundle.ts deleted file mode 100644 index 8ec6e3f..0000000 --- a/build/bundle.ts +++ /dev/null @@ -1,42 +0,0 @@ -import browserify from "browserify"; -import fs from "fs"; - -function bundle(input: string, output: string): Promise -{ - return new Promise((resolve, reject) => - { - const bundler: browserify.BrowserifyObject = browserify(input); - bundler.external("worker_threads"); - - bundler.bundle((err, src) => - { - if (err) - reject(err); - else - { - fs.writeFile(output, src, (err) => - { - if (err) - reject(err); - else - resolve(); - }); - } - }); - }); -} - -async function main(): Promise -{ - const INSTANCES = - [ - "worker-server", - "worker-client", - "shared-worker-server", - "shared-worker-client", - "web-client" - ]; - for (const instance of INSTANCES) - await bundle(`${__dirname}/../dist/test/browser/${instance}.js`, `${__dirname}/../bundle/${instance}.js`); -} -main(); \ No newline at end of file diff --git a/build/clean.ts b/build/clean.ts deleted file mode 100644 index 1b11e09..0000000 --- a/build/clean.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as fs from "fs"; - -function main(): void -{ - const PATH = __dirname + "/../dist"; - - if (fs.existsSync(PATH)) - fs.rmdirSync(PATH, { recursive: true }); -} -main(); \ No newline at end of file diff --git a/build/dist.ts b/build/dist.ts deleted file mode 100644 index 0bbce73..0000000 --- a/build/dist.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as fs from "fs"; - -function main(): void -{ - const FILES = [ - ".npmignore", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.md", - "LICENSE", - "package.json", - "README.md" - ]; - const ROOT = `${__dirname}/..`; - const DIST = `${ROOT}/dist`; - - for (const file of FILES) - if (fs.existsSync(`${DIST}/${file}`) === false) - fs.linkSync(`${ROOT}/${file}`, `${DIST}/${file}`); -} -main(); \ No newline at end of file diff --git a/package.json b/package.json index fa9c2fc..5b80fa8 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,53 @@ { "name": "tgrid", + "version": "0.9.0", + "main": "lib/index.js", + "typings": "lib/index.d.ts", "description": "Grid Computing Framework for TypeScript", "author": { "name": "Jeongho Nam", "email": "samchon.github@gmail.com", "url": "https://github.com/samchon" }, - "version": "0.8.9", - "main": "index.js", - "typings": "index.d.ts", "scripts": { - "api": "typedoc src --exclude \"**/+(test|benchmark)/**\" --excludeNotDocumented -out ../tgrid.com/docs/api", - "build": "npm run clean && npm run compile && npm run bundle && npm run test", - "bundle": "ts-node build/bundle", - "clean": "ts-node build/clean", - "compile": "tsc", - "dev": "tsc --watch", - "eslint": "eslint src", - "eslint:fix": "eslint src --fix", - "package": "npm run build && ts-node build/dist && cd dist && npm publish", - "package:next": "npm run package -- --tag next", - "prettier": "prettier src --write", + "api": "typedoc src --excludeNotDocumented -out ../tgrid.com/docs/api", + "build": "npm run build:main && npm run build:test", + "build:main": "rimraf lib && tsc", + "build:test": "rimraf bin && tsc --project test/tsconfig.json", + "dev": "npm run build:test -- --watch", + "prepare": "ts-patch install", + "prettier": "prettier src --write && prettier test --write", "test": "npm run test:node && npm run test:browser", - "test:browser": "node dist/test/browser", - "test:node": "node dist/test/node" + "test:browser": "node bin/test/browser", + "test:node": "node bin/test/node" }, "dependencies": { "serialize-error": "^4.1.0", "tstl": "^2.5.13", - "uuid": "^9.0.0", + "uuid": "^9.0.1", "ws": "^7.5.3" }, "devDependencies": { - "@types/browserify": "^12.0.37", - "@types/node": "^16.4.4", - "@types/puppeteer": "^5.4.4", - "@types/uuid": "^8.3.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/browserify": "^12.0.40", + "@types/node": "^20.11.26", + "@types/puppeteer": "^7.0.4", + "@types/uuid": "^9.0.8", "@types/ws": "^7.4.7", "@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/parser": "^5.33.0", "browserify": "^17.0.0", - "eslint": "^8.22.0", + "eslint": "^8.57.0", "local-web-server": "^2.6.0", "mv": "^2.1.1", - "prettier": "^2.7.1", - "puppeteer": "^10.1.0", - "source-map-support": "^0.5.19", - "ts-node": "^10.1.0", - "typedoc": "^0.25.0", - "typescript": "^5.2.2", + "prettier": "^3.2.5", + "puppeteer": "^22.4.1", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.2", + "ts-patch": "^3.1.2", + "typedoc": "^0.25.12", + "typescript": "^5.4.2", + "typescript-transform-paths": "^3.4.7", "whatwg-fetch": "^3.6.2" }, "homepage": "https://tgrid.com", @@ -74,5 +73,11 @@ "worker", "shared worker", "thread" + ], + "files": [ + "LICENSE", + "README.md", + "lib", + "src" ] } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..c319b6e --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,14 @@ +module.exports = { + parser: "typescript", + printWidth: 80, + semi: true, + tabWidth: 2, + trailingComma: "all", + importOrder: [ + "", + "^[./]", + ], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + importOrderParserPlugins: ["decorators-legacy", "typescript"], +}; diff --git a/src/components/Communicator.ts b/src/components/Communicator.ts index 4b57c23..67e3034 100644 --- a/src/components/Communicator.ts +++ b/src/components/Communicator.ts @@ -1,9 +1,4 @@ -/** - * @packageDocumentation - * @module tgrid.components - */ -//---------------------------------------------------------------- -import { Driver } from "./Driver"; +import { Driver } from "../typings/Driver"; import { Invoke } from "./Invoke"; import { Pair } from "tstl/utility/Pair"; @@ -34,369 +29,366 @@ import serializeError from "serialize-error"; * @author Jeongho Nam - https://github.com/samchon */ export abstract class Communicator { - /** - * @hidden - */ - private static SEQUENCE: number = 0; - - /** - * @hidden - */ - protected provider_: Provider; - - /** - * @hidden - */ - private driver_: Driver; - - /** - * @hidden - */ - private promises_: HashMap>; - - /** - * @hidden - */ - private join_cv_: ConditionVariable; - - /* ---------------------------------------------------------------- - CONSTRUCTORS - ---------------------------------------------------------------- */ - /** - * Initializer Constructor. - * - * @param provider An object providing features for remote system. - */ - protected constructor(provider: Provider) { - // PROVIDER & DRIVER - this.provider_ = provider; - this.driver_ = new Proxy(new Driver(), { - get: ({}, name: string) => { - if (name === "then") return null; - else return this._Proxy_func(name); - }, - }) as any; - - // OTHER MEMBERS - this.promises_ = new HashMap(); - this.join_cv_ = new ConditionVariable(); - } - - /** - * Destory the communicator. - * - * A destory function must be called when the network communication has been closed. - * It would destroy all function calls in the remote system (by `Driver`), - * which are not returned yet. - * - * The *error* instance would be thrown to those function calls. If the disconnection is - * abnormal, then write the detailed reason why into the *error* instance. - * - * @param error An error instance to be thrown to the unreturned functions. - */ - protected async destructor(error?: Error): Promise { - // REJECT UNRETURNED FUNCTIONS - const rejectError: Error = error - ? error - : new RuntimeError("Connection has been closed."); - - for (const entry of this.promises_) { - const reject: FunctionLike = entry.second.second; - reject(rejectError); - } - - // CLEAR PROMISES - this.promises_.clear(); - - // RESOLVE JOINERS - await this.join_cv_.notify_all(); - } - - /** - * A predicator inspects whether the *network communication* is on ready. - * - * @param method The method name for tracing. - */ - protected abstract inspectReady(method: string): Error | null; - - /** - * @hidden - */ - private _Proxy_func(name: string): FunctionLike { - const func = (...params: any[]) => this._Call_function(name, ...params); - - return new Proxy(func, { - get: ({}, newName: string) => { - if (newName === "bind") - return (thisArg: any, ...args: any[]) => - func.bind(thisArg, ...args); - else if (newName === "call") - return (thisArg: any, ...args: any[]) => - func.call(thisArg, ...args); - else if (newName === "apply") - return (thisArg: any, args: any[]) => - func.apply(thisArg, args); - - return this._Proxy_func(`${name}.${newName}`); - }, - }); - } - - /** - * @hidden - */ - private _Call_function(name: string, ...params: any[]): Promise { - return new Promise(async (resolve, reject) => { - // READY TO SEND ? - const error: Error | null = this.inspectReady( - "Communicator._Call_fuction", - ); - if (error) { - reject(error); - return; - } - - // CONSTRUCT INVOKE MESSAGE - const invoke: Invoke.IFunction = { - uid: ++Communicator.SEQUENCE, - listener: name, - parameters: params.map((p) => ({ - type: typeof p, - value: p, - })), - }; - - // DO SEND WITH PROMISE - this.promises_.emplace(invoke.uid, new Pair(resolve, reject)); - await this.sendData(invoke); - }); - } - - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Set `Provider` - * - * @param obj An object would be provided for remote system. - */ - public setProvider(obj: Provider): void { - this.provider_ = obj; - } - - /** - * Get current `Provider`. - * - * Get an object providing features (functions & objects) for remote system. The remote - * system would call the features (`Provider`) by using its `Driver`. - * - * @return Current `Provider` object - */ - public getProvider(): Provider { - return this.provider_; - } - - /** - * Get Driver for RFC (Remote Function Call). - * - * The `Controller` is an interface who defines provided functions from the remote - * system. The `Driver` is an object who makes to call remote functions, defined in - * the `Controller` and provided by `Provider` in the remote system, possible. - * - * In other words, calling a functions in the `Driver`, it means to call - * a matched function in the remote system's `Provider` object. - * - * - `Controller`: Definition only - * - `Driver`: Remote Function Call - * - * @template Controller An interface for provided features (functions & objects) from the remote system (`Provider`). - * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. - * @return A Driver for the RFC. - */ - public getDriver< - Controller extends object, - UseParametric extends boolean = false, - >(): Driver { - return this.driver_ as Driver; - } - - /** - * Join connection. - * - * Wait until the connection to be closed. - */ - public join(): Promise; - - /** - * Join connection or timeout. - * - * Wait until the connection to be clsoed until timeout. - * - * @param ms The maximum milliseconds for joining. - * @return Whether awaken by disconnection or timeout. - */ - public join(ms: number): Promise; - - /** - * Join connection or time expiration. - * - * Wait until the connection to be closed until time expiration. - * - * @param at The maximum time point to join. - * @return Whether awaken by disconnection or time expiration. - */ - public join(at: Date): Promise; - - public async join(param?: number | Date): Promise { - // IS JOINABLE ? - const error: Error | null = this.inspectReady( - `${this.constructor.name}.join`, - ); - if (error) throw error; - - // FUNCTION OVERLOADINGS - if (param === undefined) await this.join_cv_.wait(); - else if (param instanceof Date) - return await this.join_cv_.wait_until(param); - else return await this.join_cv_.wait_for(param); + /** + * @hidden + */ + private static SEQUENCE: number = 0; + + /** + * @hidden + */ + protected provider_: Provider; + + /** + * @hidden + */ + private driver_: Driver; + + /** + * @hidden + */ + private promises_: HashMap>; + + /** + * @hidden + */ + private join_cv_: ConditionVariable; + + /* ---------------------------------------------------------------- + CONSTRUCTORS + ---------------------------------------------------------------- */ + /** + * Initializer Constructor. + * + * @param provider An object providing features for remote system. + */ + protected constructor(provider: Provider) { + // PROVIDER & DRIVER + this.provider_ = provider; + this.driver_ = new Proxy(new Driver(), { + get: ({}, name: string) => { + if (name === "then") return null; + else return this._Proxy_func(name); + }, + }) as any; + + // OTHER MEMBERS + this.promises_ = new HashMap(); + this.join_cv_ = new ConditionVariable(); + } + + /** + * Destory the communicator. + * + * A destory function must be called when the network communication has been closed. + * It would destroy all function calls in the remote system (by `Driver`), + * which are not returned yet. + * + * The *error* instance would be thrown to those function calls. If the disconnection is + * abnormal, then write the detailed reason why into the *error* instance. + * + * @param error An error instance to be thrown to the unreturned functions. + */ + protected async destructor(error?: Error): Promise { + // REJECT UNRETURNED FUNCTIONS + const rejectError: Error = error + ? error + : new RuntimeError("Connection has been closed."); + + for (const entry of this.promises_) { + const reject: FunctionLike = entry.second.second; + reject(rejectError); } - /* ================================================================ + // CLEAR PROMISES + this.promises_.clear(); + + // RESOLVE JOINERS + await this.join_cv_.notify_all(); + } + + /** + * A predicator inspects whether the *network communication* is on ready. + * + * @param method The method name for tracing. + */ + protected abstract inspectReady(method: string): Error | null; + + /** + * @hidden + */ + private _Proxy_func(name: string): FunctionLike { + const func = (...params: any[]) => this._Call_function(name, ...params); + + return new Proxy(func, { + get: ({}, newName: string) => { + if (newName === "bind") + return (thisArg: any, ...args: any[]) => func.bind(thisArg, ...args); + else if (newName === "call") + return (thisArg: any, ...args: any[]) => func.call(thisArg, ...args); + else if (newName === "apply") + return (thisArg: any, args: any[]) => func.apply(thisArg, args); + + return this._Proxy_func(`${name}.${newName}`); + }, + }); + } + + /** + * @hidden + */ + private _Call_function(name: string, ...params: any[]): Promise { + return new Promise(async (resolve, reject) => { + // READY TO SEND ? + const error: Error | null = this.inspectReady( + "Communicator._Call_fuction", + ); + if (error) { + reject(error); + return; + } + + // CONSTRUCT INVOKE MESSAGE + const invoke: Invoke.IFunction = { + uid: ++Communicator.SEQUENCE, + listener: name, + parameters: params.map((p) => ({ + type: typeof p, + value: p, + })), + }; + + // DO SEND WITH PROMISE + this.promises_.emplace(invoke.uid, new Pair(resolve, reject)); + await this.sendData(invoke); + }); + } + + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Set `Provider` + * + * @param obj An object would be provided for remote system. + */ + public setProvider(obj: Provider): void { + this.provider_ = obj; + } + + /** + * Get current `Provider`. + * + * Get an object providing features (functions & objects) for remote system. The remote + * system would call the features (`Provider`) by using its `Driver`. + * + * @return Current `Provider` object + */ + public getProvider(): Provider { + return this.provider_; + } + + /** + * Get Driver for RFC (Remote Function Call). + * + * The `Controller` is an interface who defines provided functions from the remote + * system. The `Driver` is an object who makes to call remote functions, defined in + * the `Controller` and provided by `Provider` in the remote system, possible. + * + * In other words, calling a functions in the `Driver`, it means to call + * a matched function in the remote system's `Provider` object. + * + * - `Controller`: Definition only + * - `Driver`: Remote Function Call + * + * @template Controller An interface for provided features (functions & objects) from the remote system (`Provider`). + * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. + * @return A Driver for the RFC. + */ + public getDriver< + Controller extends object, + UseParametric extends boolean = false, + >(): Driver { + return this.driver_ as Driver; + } + + /** + * Join connection. + * + * Wait until the connection to be closed. + */ + public join(): Promise; + + /** + * Join connection or timeout. + * + * Wait until the connection to be clsoed until timeout. + * + * @param ms The maximum milliseconds for joining. + * @return Whether awaken by disconnection or timeout. + */ + public join(ms: number): Promise; + + /** + * Join connection or time expiration. + * + * Wait until the connection to be closed until time expiration. + * + * @param at The maximum time point to join. + * @return Whether awaken by disconnection or time expiration. + */ + public join(at: Date): Promise; + + public async join(param?: number | Date): Promise { + // IS JOINABLE ? + const error: Error | null = this.inspectReady( + `${this.constructor.name}.join`, + ); + if (error) throw error; + + // FUNCTION OVERLOADINGS + if (param === undefined) await this.join_cv_.wait(); + else if (param instanceof Date) + return await this.join_cv_.wait_until(param); + else return await this.join_cv_.wait_for(param); + } + + /* ================================================================ COMMUNICATORS - REPLIER - SENDER =================================================================== REPLIER ---------------------------------------------------------------- */ - /** - * Data Reply Function. - * - * A function should be called when data has come from the remote system. - * - * When you receive a message from the remote system, then parse the message with your - * special protocol and covert it to be an *Invoke* object. After the conversion, call - * this method. - * - * @param invoke Structured data converted by your special protocol. - */ - protected replyData(invoke: Invoke): void { - if ((invoke as Invoke.IFunction).listener) - this._Handle_function(invoke as Invoke.IFunction).catch(() => {}); - else this._Handle_return(invoke as Invoke.IReturn); - } + /** + * Data Reply Function. + * + * A function should be called when data has come from the remote system. + * + * When you receive a message from the remote system, then parse the message with your + * special protocol and covert it to be an *Invoke* object. After the conversion, call + * this method. + * + * @param invoke Structured data converted by your special protocol. + */ + protected replyData(invoke: Invoke): void { + if ((invoke as Invoke.IFunction).listener) + this._Handle_function(invoke as Invoke.IFunction).catch(() => {}); + else this._Handle_return(invoke as Invoke.IReturn); + } + + /** + * @hidden + */ + private async _Handle_function(invoke: Invoke.IFunction): Promise { + const uid: number = invoke.uid; + + try { + //---- + // FIND FUNCTION + //---- + if (this.provider_ === undefined) + // PROVIDER MUST BE + throw new RuntimeError( + `Error on Communicator._Handle_function(): the provider is not specified yet.`, + ); + else if (this.provider_ === null) + throw new DomainError( + "Error on Communicator._Handle_function(): the provider would not be.", + ); - /** - * @hidden - */ - private async _Handle_function(invoke: Invoke.IFunction): Promise { - const uid: number = invoke.uid; - - try { - //---- - // FIND FUNCTION - //---- - if (this.provider_ === undefined) - // PROVIDER MUST BE - throw new RuntimeError( - `Error on Communicator._Handle_function(): the provider is not specified yet.`, - ); - else if (this.provider_ === null) - throw new DomainError( - "Error on Communicator._Handle_function(): the provider would not be.", - ); - - // FIND FUNCTION (WITH THIS-ARG) - let func: FunctionLike = this.provider_ as any; - let thisArg: any = undefined; - - const routes: string[] = invoke.listener.split("."); - for (const name of routes) { - thisArg = func; - func = thisArg[name]; - - // SECURITY-ERRORS - if (name[0] === "_") - throw new RuntimeError( - `Error on Communicator._Handle_function(): RFC does not allow access to a member starting with the underscore: Provider.${invoke.listener}()`, - ); - else if (name[name.length - 1] === "_") - throw new RuntimeError( - `Error on Communicator._Handle_function(): RFC does not allow access to a member ending with the underscore: Provider.${invoke.listener}().`, - ); - else if (name === "toString" && func === Function.toString) - throw new RuntimeError( - `Error on Communicator._Handle_function(): RFC on Function.toString() is not allowed: Provider.${invoke.listener}().`, - ); - else if (name === "constructor" || name === "prototype") - throw new RuntimeError( - `Error on Communicator._Handle_function(): RFC does not allow access to ${name}: Provider.${invoke.listener}().`, - ); - } - func = func.bind(thisArg); - - //---- - // RETURN VALUE - //---- - // CALL FUNCTION - const parameters: any[] = invoke.parameters.map((p) => p.value); - const ret: any = await func(...parameters); - - await this._Send_return(uid, true, ret); - } catch (exp) { - await this._Send_return(uid, false, exp); - } + // FIND FUNCTION (WITH THIS-ARG) + let func: FunctionLike = this.provider_ as any; + let thisArg: any = undefined; + + const routes: string[] = invoke.listener.split("."); + for (const name of routes) { + thisArg = func; + func = thisArg[name]; + + // SECURITY-ERRORS + if (name[0] === "_") + throw new RuntimeError( + `Error on Communicator._Handle_function(): RFC does not allow access to a member starting with the underscore: Provider.${invoke.listener}()`, + ); + else if (name[name.length - 1] === "_") + throw new RuntimeError( + `Error on Communicator._Handle_function(): RFC does not allow access to a member ending with the underscore: Provider.${invoke.listener}().`, + ); + else if (name === "toString" && func === Function.toString) + throw new RuntimeError( + `Error on Communicator._Handle_function(): RFC on Function.toString() is not allowed: Provider.${invoke.listener}().`, + ); + else if (name === "constructor" || name === "prototype") + throw new RuntimeError( + `Error on Communicator._Handle_function(): RFC does not allow access to ${name}: Provider.${invoke.listener}().`, + ); + } + func = func.bind(thisArg); + + //---- + // RETURN VALUE + //---- + // CALL FUNCTION + const parameters: any[] = invoke.parameters.map((p) => p.value); + const ret: any = await func(...parameters); + + await this._Send_return(uid, true, ret); + } catch (exp) { + await this._Send_return(uid, false, exp); } - - /** - * @hidden - */ - private _Handle_return(invoke: Invoke.IReturn): void { - // GET THE PROMISE OBJECT - const it = this.promises_.find(invoke.uid); - if (it.equals(this.promises_.end())) return; - - // RETURNS - const func: FunctionLike = invoke.success - ? it.second.first - : it.second.second; - this.promises_.erase(it); - - func(invoke.value); + } + + /** + * @hidden + */ + private _Handle_return(invoke: Invoke.IReturn): void { + // GET THE PROMISE OBJECT + const it = this.promises_.find(invoke.uid); + if (it.equals(this.promises_.end())) return; + + // RETURNS + const func: FunctionLike = invoke.success + ? it.second.first + : it.second.second; + this.promises_.erase(it); + + func(invoke.value); + } + + /* ---------------------------------------------------------------- + SENDER + ---------------------------------------------------------------- */ + /** + * A function sending data to the remote system. + * + * @param invoke Structured data to send. + */ + protected abstract sendData(invoke: Invoke): Promise; + + /** + * @hidden + */ + private async _Send_return( + uid: number, + flag: boolean, + val: any, + ): Promise { + // SPECIAL LOGIC FOR ERROR -> FOR CLEAR JSON ENCODING + if (flag === false && val instanceof Error) { + if (typeof (val as Exception).toJSON === "function") + val = (val as Exception).toJSON(); + else val = serializeError(val); } - /* ---------------------------------------------------------------- - SENDER - ---------------------------------------------------------------- */ - /** - * A function sending data to the remote system. - * - * @param invoke Structured data to send. - */ - protected abstract sendData(invoke: Invoke): Promise; - - /** - * @hidden - */ - private async _Send_return( - uid: number, - flag: boolean, - val: any, - ): Promise { - // SPECIAL LOGIC FOR ERROR -> FOR CLEAR JSON ENCODING - if (flag === false && val instanceof Error) { - if (typeof (val as Exception).toJSON === "function") - val = (val as Exception).toJSON(); - else val = serializeError(val); - } - - // RETURNS - const ret: Invoke.IReturn = { - uid: uid, - success: flag, - value: val, - }; - await this.sendData(ret); - } + // RETURNS + const ret: Invoke.IReturn = { + uid: uid, + success: flag, + value: val, + }; + await this.sendData(ret); + } } type FunctionLike = (...args: any[]) => any; diff --git a/src/components/Invoke.ts b/src/components/Invoke.ts index cac38cb..7e06f73 100644 --- a/src/components/Invoke.ts +++ b/src/components/Invoke.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.components - */ -//---------------------------------------------------------------- /** * Message structure for RFC (Remote Function Call). * @@ -11,48 +6,48 @@ export type Invoke = Invoke.IFunction | Invoke.IReturn; export namespace Invoke { + /** + * Message for Requesting RFC. + */ + export interface IFunction extends IBase { /** - * Message for Requesting RFC. + * Target function (sometimes calsuled in objects) to call. */ - export interface IFunction extends IBase { - /** - * Target function (sometimes calsuled in objects) to call. - */ - listener: string; + listener: string; - /** - * Parameters for the function call. - */ - parameters: IParameter[]; - } + /** + * Parameters for the function call. + */ + parameters: IParameter[]; + } - export interface IParameter { - type: string; - value: any; - } + export interface IParameter { + type: string; + value: any; + } + /** + * Message for Returning RFC. + */ + export interface IReturn extends IBase { /** - * Message for Returning RFC. + * `true` -> return, `false` -> exception. */ - export interface IReturn extends IBase { - /** - * `true` -> return, `false` -> exception. - */ - success: boolean; + success: boolean; - /** - * Returned value or thrown exception. - */ - value: any; - } + /** + * Returned value or thrown exception. + */ + value: any; + } + /** + * @hiden + */ + interface IBase { /** - * @hiden + * Unique identifier. */ - interface IBase { - /** - * Unique identifier. - */ - uid: number; - } + uid: number; + } } diff --git a/src/components/index.ts b/src/components/index.ts index 3daba26..ffeb208 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,9 +1,2 @@ -/** - * @packageDocumentation - * @module tgrid.components - */ -//---------------------------------------------------------------- -import * as components from "./module"; - -export default components; -export * from "./module"; +export * from "./Communicator"; +export * from "./Invoke"; diff --git a/src/components/module.ts b/src/components/module.ts deleted file mode 100644 index ce0cab1..0000000 --- a/src/components/module.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.components - */ -//---------------------------------------------------------------- -export * from "./Communicator"; -export * from "./Invoke"; -export * from "./Driver"; diff --git a/src/index.ts b/src/index.ts index 4f8cbb4..d4ede1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid - */ -//---------------------------------------------------------------- import * as tgrid from "./module"; export default tgrid; diff --git a/src/module.ts b/src/module.ts index f901436..ee7dda0 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,10 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid - */ -//---------------------------------------------------------------- -import * as components from "./components/module"; -import * as protocols from "./protocols/module"; -import * as utils from "./utils/module"; - -export { components, protocols, utils }; +export * from "./components"; +export * from "./protocols"; +export * from "./typings"; diff --git a/src/protocols/index.ts b/src/protocols/index.ts index b496eb8..2582305 100644 --- a/src/protocols/index.ts +++ b/src/protocols/index.ts @@ -1,9 +1,2 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- -import * as protocols from "./module"; - -export default protocols; -export * from "./module"; +export * from "./web"; +export * from "./workers"; diff --git a/src/protocols/internal/AcceptorBase.ts b/src/protocols/internal/AcceptorBase.ts index 1c254a6..0f3c24a 100644 --- a/src/protocols/internal/AcceptorBase.ts +++ b/src/protocols/internal/AcceptorBase.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- import { Communicator } from "../../components/Communicator"; import { DomainError } from "tstl/exception/DomainError"; @@ -30,113 +25,113 @@ import { RuntimeError } from "tstl/exception/RuntimeError"; * @author Jeongho Nam - https://github.com/samchon */ export abstract class AcceptorBase< - Header, - Provider extends object | null, + Header, + Provider extends object | null, > extends Communicator { - /** - * @hidden - */ - private readonly header_: Header; + /** + * @hidden + */ + private readonly header_: Header; - /** - * @hidden - */ - protected state_: AcceptorBase.State; + /** + * @hidden + */ + protected state_: AcceptorBase.State; - /* ---------------------------------------------------------------- - ACCEPTIONS - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected constructor(header: Header) { - super(undefined); + /* ---------------------------------------------------------------- + CONSTRUCTORS + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected constructor(header: Header) { + super(undefined); - this.header_ = header; - this.state_ = AcceptorBase.State.NONE; - } + this.header_ = header; + this.state_ = AcceptorBase.State.NONE; + } - /** - * Accept connection. - * - * Accepts (permits) the client's connection with this server and starts interaction. - * - * @param provider An object providing features for remote system. - */ - protected abstract accept(provider: Provider | null): Promise; + /** + * Accept connection. + * + * Accepts (permits) the client's connection with this server and starts interaction. + * + * @param provider An object providing features for remote system. + */ + protected abstract accept(provider: Provider | null): Promise; - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Header containing initialization data like activation. - */ - public get header(): Header { - return this.header_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Header containing initialization data like activation. + */ + public get header(): Header { + return this.header_; + } - /** - * Get state. - * - * Get current state of connection state with the remote client. - * - * List of values are such like below: - * - * - `REJECTING`: The `reject` method is on running. - * - `NONE`: This instance is newly created, but did nothing yet. - * - `ACCEPTING`: The `accept` method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The `close` method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): AcceptorBase.State { - return this.state_; - } + /** + * Get state. + * + * Get current state of connection state with the remote client. + * + * List of values are such like below: + * + * - `REJECTING`: The `reject` method is on running. + * - `NONE`: This instance is newly created, but did nothing yet. + * - `ACCEPTING`: The `accept` method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The `close` method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): AcceptorBase.State { + return this.state_; + } - /** - * @hidden - */ - protected inspectReady(method: string): Error | null { - // NO ERROR - if (this.state_ === AcceptorBase.State.OPEN) return null; - // ERROR, ONE OF THEM - else if (this.state_ === AcceptorBase.State.NONE) - return new DomainError( - `Error on ${this.constructor.name}.${method}(): not accepted yet.`, - ); - else if (this.state_ === AcceptorBase.State.ACCEPTING) - return new DomainError( - `Error on ${this.constructor.name}.${method}(): it's on accepting, wait for a second.`, - ); - else if ( - this.state_ === AcceptorBase.State.REJECTING || - AcceptorBase.State.CLOSING - ) - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): the connection is on closing.`, - ); - else if (this.state_ === AcceptorBase.State.CLOSED) - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): the connection has been closed.`, - ); - // UNKNOWN ERROR, IT MAY NOT OCCURED - else - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): unknown error, but not connected.`, - ); - } + /** + * @hidden + */ + protected inspectReady(method: string): Error | null { + // NO ERROR + if (this.state_ === AcceptorBase.State.OPEN) return null; + // ERROR, ONE OF THEM + else if (this.state_ === AcceptorBase.State.NONE) + return new DomainError( + `Error on ${this.constructor.name}.${method}(): not accepted yet.`, + ); + else if (this.state_ === AcceptorBase.State.ACCEPTING) + return new DomainError( + `Error on ${this.constructor.name}.${method}(): it's on accepting, wait for a second.`, + ); + else if ( + this.state_ === AcceptorBase.State.REJECTING || + AcceptorBase.State.CLOSING + ) + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): the connection is on closing.`, + ); + else if (this.state_ === AcceptorBase.State.CLOSED) + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): the connection has been closed.`, + ); + // UNKNOWN ERROR, IT MAY NOT OCCURED + else + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): unknown error, but not connected.`, + ); + } } export namespace AcceptorBase { - /** - * Current state type of acceptor. - */ - export const enum State { - REJECTING = -2, - NONE, - ACCEPTING, - OPEN, - CLOSING, - CLOSED, - } + /** + * Current state type of acceptor. + */ + export const enum State { + REJECTING = -2, + NONE, + ACCEPTING, + OPEN, + CLOSING, + CLOSED, + } } diff --git a/src/protocols/internal/ConnectorBase.ts b/src/protocols/internal/ConnectorBase.ts index e3b200b..c1535c3 100644 --- a/src/protocols/internal/ConnectorBase.ts +++ b/src/protocols/internal/ConnectorBase.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- import { Communicator } from "../../components/Communicator"; import { DomainError } from "tstl/exception/DomainError"; @@ -28,105 +23,105 @@ import { RuntimeError } from "tstl/exception/RuntimeError"; * @author Jeongho Nam - https://github.com/samchon */ export abstract class ConnectorBase< - Header, - Provider extends object | null, + Header, + Provider extends object | null, > extends Communicator { - /** - * @hidden - */ - private readonly header_: Header; + /** + * @hidden + */ + private readonly header_: Header; - /** - * @hidden - */ - protected state_: ConnectorBase.State; + /** + * @hidden + */ + protected state_: ConnectorBase.State; - /* ---------------------------------------------------------------- - CONSTRUCTOR - ---------------------------------------------------------------- */ - /** - * Initializer Constructor. - * - * @param header An object containing initialization data like activation. - * @param provider An object providing features for remote system. - */ - public constructor(header: Header, provider: Provider) { - super(provider); + /* ---------------------------------------------------------------- + CONSTRUCTORS + ---------------------------------------------------------------- */ + /** + * Initializer Constructor. + * + * @param header An object containing initialization data like activation. + * @param provider An object providing features for remote system. + */ + public constructor(header: Header, provider: Provider) { + super(provider); - this.header_ = header; - this.state_ = ConnectorBase.State.NONE; - } + this.header_ = header; + this.state_ = ConnectorBase.State.NONE; + } - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Header containing initialization data like activation. - */ - public get header(): Header { - return this.header_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Header containing initialization data like activation. + */ + public get header(): Header { + return this.header_; + } - /** - * Get state. - * - * Get current state of connection state with the worker server. - * - * List of values are such like below: - * - * - `NONE`: This instance is newly created, but did nothing yet. - * - `CONNECTING`: The `connect` method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The `close` method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): ConnectorBase.State { - return this.state_; - } + /** + * Get state. + * + * Get current state of connection state with the worker server. + * + * List of values are such like below: + * + * - `NONE`: This instance is newly created, but did nothing yet. + * - `CONNECTING`: The `connect` method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The `close` method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): ConnectorBase.State { + return this.state_; + } - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected inspectReady(method: string): Error | null { - // NO ERROR - if (this.state_ === ConnectorBase.State.OPEN) return null; - // ERROR, ONE OF THEM - else if (this.state_ === ConnectorBase.State.NONE) - return new DomainError( - `Error on ${this.constructor.name}.${method}(): connect first.`, - ); - else if (this.state_ === ConnectorBase.State.CONNECTING) - return new DomainError( - `Error on ${this.constructor.name}.${method}(): it's on connecting, wait for a second.`, - ); - else if (this.state_ === ConnectorBase.State.CLOSING) - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): the connection is on closing.`, - ); - else if (this.state_ === ConnectorBase.State.CLOSED) - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): the connection has been closed.`, - ); - // UNKNOWN ERROR, IT MAY NOT OCCURED - else - return new RuntimeError( - `Error on ${this.constructor.name}.${method}(): unknown error, but not connected.`, - ); - } + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected inspectReady(method: string): Error | null { + // NO ERROR + if (this.state_ === ConnectorBase.State.OPEN) return null; + // ERROR, ONE OF THEM + else if (this.state_ === ConnectorBase.State.NONE) + return new DomainError( + `Error on ${this.constructor.name}.${method}(): connect first.`, + ); + else if (this.state_ === ConnectorBase.State.CONNECTING) + return new DomainError( + `Error on ${this.constructor.name}.${method}(): it's on connecting, wait for a second.`, + ); + else if (this.state_ === ConnectorBase.State.CLOSING) + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): the connection is on closing.`, + ); + else if (this.state_ === ConnectorBase.State.CLOSED) + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): the connection has been closed.`, + ); + // UNKNOWN ERROR, IT MAY NOT OCCURED + else + return new RuntimeError( + `Error on ${this.constructor.name}.${method}(): unknown error, but not connected.`, + ); + } } export namespace ConnectorBase { - /** - * Current state type of connector. - */ - export const enum State { - NONE = -1, - CONNECTING, - OPEN, - CLOSING, - CLOSED, - } + /** + * Current state type of connector. + */ + export const enum State { + NONE = -1, + CONNECTING, + OPEN, + CLOSING, + CLOSED, + } } diff --git a/src/protocols/internal/IHeaderWrapper.ts b/src/protocols/internal/IHeaderWrapper.ts index cc5b4a1..46a04f8 100644 --- a/src/protocols/internal/IHeaderWrapper.ts +++ b/src/protocols/internal/IHeaderWrapper.ts @@ -1,22 +1,17 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- /** * @hidden */ export interface IHeaderWrapper { - header: Headers; + header: Headers; } /** * @hidden */ export namespace IHeaderWrapper { - export function wrap
(header: Header): IHeaderWrapper
{ - return { - header: header, - }; - } + export function wrap
(header: Header): IHeaderWrapper
{ + return { + header: header, + }; + } } diff --git a/src/protocols/internal/IServer.ts b/src/protocols/internal/IServer.ts index 03bee94..0c5cd92 100644 --- a/src/protocols/internal/IServer.ts +++ b/src/protocols/internal/IServer.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- /** * Common interface for server. * @@ -10,17 +5,17 @@ * @author Jeongho Nam - https://github.com/samchon */ export interface IServer { - /** - * Current state of the server. - */ - readonly state: State; + /** + * Current state of the server. + */ + readonly state: State; } export namespace IServer { - export const enum State { - NONE = -1, - OPENING, - OPEN, - CLOSING, - CLOSED, - } + export const enum State { + NONE = -1, + OPENING, + OPEN, + CLOSING, + CLOSED, + } } diff --git a/src/protocols/internal/once.ts b/src/protocols/internal/once.ts index 36b9b49..4d38a59 100644 --- a/src/protocols/internal/once.ts +++ b/src/protocols/internal/once.ts @@ -1,20 +1,15 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- /** * @hidden */ export function once(handler: Func): Func { - let called: boolean = false; - let ret: any = undefined; + let called: boolean = false; + let ret: any = undefined; - return ((...args: any) => { - if (called === false) { - ret = (handler as any)(...args); - called = true; - } - return ret; - }) as any; + return ((...args: any) => { + if (called === false) { + ret = (handler as any)(...args); + called = true; + } + return ret; + }) as any; } diff --git a/src/protocols/module.ts b/src/protocols/module.ts deleted file mode 100644 index 05b88b6..0000000 --- a/src/protocols/module.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.protocols - */ -//---------------------------------------------------------------- -import * as web from "./web/module"; -import * as workers from "./workers/module"; - -export { web, workers }; diff --git a/src/protocols/web/WebAcceptor.ts b/src/protocols/web/WebAcceptor.ts index 83395da..e3c813b 100644 --- a/src/protocols/web/WebAcceptor.ts +++ b/src/protocols/web/WebAcceptor.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- import type http from "http"; import type WebSocket from "ws"; import { DomainError } from "tstl/exception/DomainError"; @@ -43,199 +38,199 @@ import { WebError } from "./WebError"; * @author Jeongho Nam - https://github.com/samchon */ export class WebAcceptor - extends AcceptorBase - implements IWebCommunicator + extends AcceptorBase + implements IWebCommunicator { - /** - * @hidden - */ - private request_: http.IncomingMessage; - - /** - * @hidden - */ - private socket_: WebSocket; - - /* ---------------------------------------------------------------- - CONSTRUCTORS - ---------------------------------------------------------------- */ - /** - * @internal - */ - public static create( - request: http.IncomingMessage, - socket: WebSocket, - header: Header, - ): WebAcceptor { - return new WebAcceptor(request, socket, header); - } - - /** - * @hidden - */ - private constructor( - request: http.IncomingMessage, - socket: WebSocket, - header: Header, - ) { - super(header); - - this.request_ = request; - this.socket_ = socket; - } - - /** - * @inheritDoc - */ - public async close(code?: number, reason?: string): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady("close"); - if (error) throw error; - - //---- - // CLOSE WITH JOIN - //---- - // PREPARE LAZY RETURN - const ret: Promise = this.join(); - - // DO CLOSE - this.state_ = WebAcceptor.State.CLOSING; - if (code === 1000) this.socket_!.close(); - else this.socket_!.close(code!, reason!); - - // state would be closed in destructor() via _Handle_close() - await ret; + /** + * @hidden + */ + private request_: http.IncomingMessage; + + /** + * @hidden + */ + private socket_: WebSocket; + + /* ---------------------------------------------------------------- + CONSTRUCTORS + ---------------------------------------------------------------- */ + /** + * @internal + */ + public static create( + request: http.IncomingMessage, + socket: WebSocket, + header: Header, + ): WebAcceptor { + return new WebAcceptor(request, socket, header); + } + + /** + * @hidden + */ + private constructor( + request: http.IncomingMessage, + socket: WebSocket, + header: Header, + ) { + super(header); + + this.request_ = request; + this.socket_ = socket; + } + + /** + * @inheritDoc + */ + public async close(code?: number, reason?: string): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady("close"); + if (error) throw error; + + //---- + // CLOSE WITH JOIN + //---- + // PREPARE LAZY RETURN + const ret: Promise = this.join(); + + // DO CLOSE + this.state_ = WebAcceptor.State.CLOSING; + if (code === 1000) this.socket_!.close(); + else this.socket_!.close(code!, reason!); + + // state would be closed in destructor() via _Handle_close() + await ret; + } + + /** + * @hidden + */ + protected async destructor(error?: Error): Promise { + await super.destructor(error); + this.state_ = WebAcceptor.State.CLOSED; + } + + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * IP Address of client. + */ + public get ip(): string { + return this.request_.connection.remoteAddress!; + } + + /** + * Path of client has connected. + */ + public get path(): string { + return this.request_.url!; + } + + /** + * Get state. + * + * Get current state of connection state with the remote client. + * + * List of values are such like below: + * + * - `REJECTING`: The {@link WebAcceptor.reject} method is on running. + * - `NONE`: The {@link WebAcceptor} instance is newly created, but did nothing yet. + * - `ACCEPTING`: The {@link WebAcceptor.accept} method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The {@link WebAcceptor.close} method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): WebAcceptor.State { + return this.state_; + } + + /* ---------------------------------------------------------------- + HANDSHAKES + ---------------------------------------------------------------- */ + /** + * @inheritDoc + */ + public async accept(provider: Provider): Promise { + // VALIDATION + if (this.state_ !== WebAcceptor.State.NONE) + throw new DomainError( + "Error on WebAcceptor.accept(): you've already accepted (or rejected) the connection.", + ); + + // PREPARE ASSETS + this.state_ = WebAcceptor.State.ACCEPTING; + this.provider_ = provider; + + // REGISTER EVENTS + this.socket_.on("message", this._Handle_message.bind(this)); + this.socket_.on("close", this._Handle_close.bind(this)); + this.socket_.send(WebAcceptor.State.OPEN.toString()); + + // FINISHED + this.state_ = WebAcceptor.State.OPEN; + } + + /** + * Reject connection. + * + * Reject without acceptance, any interaction. The connection would be closed immediately. + * + * @param status Status code. + * @param reason Detailed reason to reject. + */ + public async reject(status?: number, reason?: string): Promise { + // VALIDATION + if (this.state_ !== WebAcceptor.State.NONE) + throw new DomainError( + "You've already accepted (or rejected) the connection.", + ); + + // SEND CLOSING FRAME + this.state_ = WebAcceptor.State.REJECTING; + this.socket_.close(status, reason); + + // FINALIZATION + await this.destructor(); + } + + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + this.socket_.send(JSON.stringify(invoke)); + } + + /** + * @hidden + */ + private _Handle_message(data: WebSocket.Data): void { + if (typeof data === "string") { + const invoke: Invoke = JSON.parse(data); + this.replyData(invoke); } + } - /** - * @hidden - */ - protected async destructor(error?: Error): Promise { - await super.destructor(error); - this.state_ = WebAcceptor.State.CLOSED; - } - - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * IP Address of client. - */ - public get ip(): string { - return this.request_.connection.remoteAddress!; - } + /** + * @hidden + */ + private async _Handle_close(code: number, reason: string): Promise { + const error: WebError | undefined = + code !== 100 ? new WebError(code, reason) : undefined; - /** - * Path of client has connected. - */ - public get path(): string { - return this.request_.url!; - } - - /** - * Get state. - * - * Get current state of connection state with the remote client. - * - * List of values are such like below: - * - * - `REJECTING`: The {@link WebAcceptor.reject} method is on running. - * - `NONE`: The {@link WebAcceptor} instance is newly created, but did nothing yet. - * - `ACCEPTING`: The {@link WebAcceptor.accept} method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The {@link WebAcceptor.close} method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): WebAcceptor.State { - return this.state_; - } - - /* ---------------------------------------------------------------- - HANDSHAKES - ---------------------------------------------------------------- */ - /** - * @inheritDoc - */ - public async accept(provider: Provider): Promise { - // VALIDATION - if (this.state_ !== WebAcceptor.State.NONE) - throw new DomainError( - "Error on WebAcceptor.accept(): you've already accepted (or rejected) the connection.", - ); - - // PREPARE ASSETS - this.state_ = WebAcceptor.State.ACCEPTING; - this.provider_ = provider; - - // REGISTER EVENTS - this.socket_.on("message", this._Handle_message.bind(this)); - this.socket_.on("close", this._Handle_close.bind(this)); - this.socket_.send(WebAcceptor.State.OPEN.toString()); - - // FINISHED - this.state_ = WebAcceptor.State.OPEN; - } - - /** - * Reject connection. - * - * Reject without acceptance, any interaction. The connection would be closed immediately. - * - * @param status Status code. - * @param reason Detailed reason to reject. - */ - public async reject(status?: number, reason?: string): Promise { - // VALIDATION - if (this.state_ !== WebAcceptor.State.NONE) - throw new DomainError( - "You've already accepted (or rejected) the connection.", - ); - - // SEND CLOSING FRAME - this.state_ = WebAcceptor.State.REJECTING; - this.socket_.close(status, reason); - - // FINALIZATION - await this.destructor(); - } - - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - this.socket_.send(JSON.stringify(invoke)); - } - - /** - * @hidden - */ - private _Handle_message(data: WebSocket.Data): void { - if (typeof data === "string") { - const invoke: Invoke = JSON.parse(data); - this.replyData(invoke); - } - } - - /** - * @hidden - */ - private async _Handle_close(code: number, reason: string): Promise { - const error: WebError | undefined = - code !== 100 ? new WebError(code, reason) : undefined; - - await this.destructor(error); - } + await this.destructor(error); + } } /** * */ export namespace WebAcceptor { - /** - * Current state of the {@link WebAcceptor}. - */ - export import State = AcceptorBase.State; + /** + * Current state of the {@link WebAcceptor}. + */ + export import State = AcceptorBase.State; } diff --git a/src/protocols/web/WebConnector.ts b/src/protocols/web/WebConnector.ts index 2cbbafa..eeb00ee 100644 --- a/src/protocols/web/WebConnector.ts +++ b/src/protocols/web/WebConnector.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- import { DomainError } from "tstl/exception/DomainError"; import { is_node } from "tstl/utility/node"; import { sleep_for } from "tstl/thread/global"; @@ -44,256 +39,254 @@ import { WebSocketPolyfill } from "./internal/WebSocketPolyfill"; * @author Jeongho Nam - https://github.com/samchon */ export class WebConnector - extends ConnectorBase - implements IWebCommunicator + extends ConnectorBase + implements IWebCommunicator { - /** - * @hidden - */ - private socket_?: WebSocket; + /** + * @hidden + */ + private socket_?: WebSocket; - /* ---------------------------------------------------------------- - CONNECTION - ---------------------------------------------------------------- */ - /** - * Connect to remote websocket server. - * - * Try connection to the remote websocket server with its address and waiting for the - * server to accept the trial. If the server rejects your connection, then exception - * would be thrown (in *Promise.catch*, as `WebError`). - * - * After the connection and your business has been completed, don't forget to closing the - * connection in time to prevent waste of the server resource. - * - * @param url URL address to connect. - * @param options Detailed options like timeout. - */ - public async connect( - url: string, - options: Partial = {}, - ): Promise { - // TEST CONDITION - if (this.socket_ && this.state !== WebConnector.State.CLOSED) - if (this.socket_.readyState === WebConnector.State.CONNECTING) - throw new DomainError( - "Error on WebConnector.connect(): already connecting.", - ); - else if (this.socket_.readyState === WebConnector.State.OPEN) - throw new DomainError( - "Error on WebConnector.connect(): already connected.", - ); - else - throw new DomainError( - "Error on WebConnector.connect(): already closing.", - ); + /* ---------------------------------------------------------------- + CONNECTION + ---------------------------------------------------------------- */ + /** + * Connect to remote websocket server. + * + * Try connection to the remote websocket server with its address and waiting for the + * server to accept the trial. If the server rejects your connection, then exception + * would be thrown (in *Promise.catch*, as `WebError`). + * + * After the connection and your business has been completed, don't forget to closing the + * connection in time to prevent waste of the server resource. + * + * @param url URL address to connect. + * @param options Detailed options like timeout. + */ + public async connect( + url: string, + options: Partial = {}, + ): Promise { + // TEST CONDITION + if (this.socket_ && this.state !== WebConnector.State.CLOSED) + if (this.socket_.readyState === WebConnector.State.CONNECTING) + throw new DomainError( + "Error on WebConnector.connect(): already connecting.", + ); + else if (this.socket_.readyState === WebConnector.State.OPEN) + throw new DomainError( + "Error on WebConnector.connect(): already connected.", + ); + else + throw new DomainError( + "Error on WebConnector.connect(): already closing.", + ); - //---- - // CONNECTION - //---- - // PREPARE ASSETS - this.state_ = WebConnector.State.CONNECTING; + //---- + // CONNECTION + //---- + // PREPARE ASSETS + this.state_ = WebConnector.State.CONNECTING; - try { - // DO CONNNECT - const factory = is_node() - ? ((await WebSocketPolyfill()) as any) - : self.WebSocket; - this.socket_ = new factory(url); - await this._Wait_connection(); + try { + // DO CONNNECT + const factory = is_node() + ? ((await WebSocketPolyfill()) as any) + : self.WebSocket; + this.socket_ = new factory(url); + await this._Wait_connection(); - // SEND HEADERS - this.socket_!.send( - JSON.stringify(IHeaderWrapper.wrap(this.header)), - ); + // SEND HEADERS + this.socket_!.send(JSON.stringify(IHeaderWrapper.wrap(this.header))); - // PROMISED HANDSHAKE - if ( - (await this._Handshake(options.timeout)) !== - WebConnector.State.OPEN.toString() - ) - throw new WebError( - 1008, - "Error on WebConnector.connect(): target server may not be opened by TGrid. It's not following the TGrid's own handshake rule.", - ); + // PROMISED HANDSHAKE + if ( + (await this._Handshake(options.timeout)) !== + WebConnector.State.OPEN.toString() + ) + throw new WebError( + 1008, + "Error on WebConnector.connect(): target server may not be opened by TGrid. It's not following the TGrid's own handshake rule.", + ); - // SUCCESS - this.state_ = WebConnector.State.OPEN; - { - this.socket_!.onmessage = this._Handle_message.bind(this); - this.socket_!.onclose = this._Handle_close.bind(this); - this.socket_!.onerror = () => {}; - } - } catch (exp) { - this.state_ = WebConnector.State.NONE; - if (this.socket_ && this.socket_.readyState === WebConnector.State.OPEN) { - this.socket_.onclose = () => {}; - this.socket_.close(); - } - throw exp; - } + // SUCCESS + this.state_ = WebConnector.State.OPEN; + { + this.socket_!.onmessage = this._Handle_message.bind(this); + this.socket_!.onclose = this._Handle_close.bind(this); + this.socket_!.onerror = () => {}; + } + } catch (exp) { + this.state_ = WebConnector.State.NONE; + if (this.socket_ && this.socket_.readyState === WebConnector.State.OPEN) { + this.socket_.onclose = () => {}; + this.socket_.close(); + } + throw exp; } + } - /** - * @hidden - */ - private _Wait_connection(): Promise { - return new Promise((resolve, reject) => { - this.socket_!.onopen = () => resolve(this.socket_!); - this.socket_!.onclose = once((evt) => { - reject(new WebError(evt.code, evt.reason)); - }); - this.socket_!.onerror = once(() => { - reject(new WebError(1006, "Connection refused.")); - }); - }); - } + /** + * @hidden + */ + private _Wait_connection(): Promise { + return new Promise((resolve, reject) => { + this.socket_!.onopen = () => resolve(this.socket_!); + this.socket_!.onclose = once((evt) => { + reject(new WebError(evt.code, evt.reason)); + }); + this.socket_!.onerror = once(() => { + reject(new WebError(1006, "Connection refused.")); + }); + }); + } - /** - * @inheritDoc - */ - public async close(code?: number, reason?: string): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady("close"); - if (error) throw error; + /** + * @inheritDoc + */ + public async close(code?: number, reason?: string): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady("close"); + if (error) throw error; - //---- - // CLOSE WITH JOIN - //---- - // PREPARE JOINER - const ret: Promise = this.join(); + //---- + // CLOSE WITH JOIN + //---- + // PREPARE JOINER + const ret: Promise = this.join(); - // DO CLOSE - this.state_ = WebConnector.State.CLOSING; - this.socket_!.close(code, reason); - - // LAZY RETURN - await ret; - } + // DO CLOSE + this.state_ = WebConnector.State.CLOSING; + this.socket_!.close(code, reason); - /** - * @hidden - */ - private _Handshake(timeout?: number): Promise { - return new Promise((resolve, reject) => { - /* eslint-disable */ - let completed: boolean = false; + // LAZY RETURN + await ret; + } - /* eslint-disable */ - let expired: boolean = false; + /** + * @hidden + */ + private _Handshake(timeout?: number): Promise { + return new Promise((resolve, reject) => { + /* eslint-disable */ + let completed: boolean = false; - // TIMEOUT - if (timeout !== undefined) - sleep_for(timeout).then(() => { - if (completed === false) { - reject( - new WebError( - 1008, - `Error on WebConnector.connect(): target server is not sending handshake data over ${timeout} milliseconds.`, - ), - ); - expired = true; - } - }); + /* eslint-disable */ + let expired: boolean = false; - // EVENT LISTENRES - this.socket_!.onmessage = once((evt) => { - if (expired === false) { - completed = true; - resolve(evt.data); - } - }); - this.socket_!.onclose = once((evt) => { - if (expired === false) { - completed = true; - reject(new WebError(evt.code, evt.reason)); - } - }); - this.socket_!.onerror = once(() => { - if (expired === false) { - completed = true; - reject(new WebError(1006, "Connection refused.")); - } - }); + // TIMEOUT + if (timeout !== undefined) + sleep_for(timeout).then(() => { + if (completed === false) { + reject( + new WebError( + 1008, + `Error on WebConnector.connect(): target server is not sending handshake data over ${timeout} milliseconds.`, + ), + ); + expired = true; + } }); - } - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Connection URL. - */ - public get url(): string | undefined { - return this.socket_ ? this.socket_.url : undefined; - } + // EVENT LISTENRES + this.socket_!.onmessage = once((evt) => { + if (expired === false) { + completed = true; + resolve(evt.data); + } + }); + this.socket_!.onclose = once((evt) => { + if (expired === false) { + completed = true; + reject(new WebError(evt.code, evt.reason)); + } + }); + this.socket_!.onerror = once(() => { + if (expired === false) { + completed = true; + reject(new WebError(1006, "Connection refused.")); + } + }); + }); + } - /** - * Get state. - * - * Get current state of connection state with the websocket server. - * - * List of values are such like below: - * - * - `NONE`: The {@link WebConnector} instance is newly created, but did nothing yet. - * - `CONNECTING`: The {@link WebConnector.connect} method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The {@link WebConnector.close} method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): WebConnector.State { - return this.state_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Connection URL. + */ + public get url(): string | undefined { + return this.socket_ ? this.socket_.url : undefined; + } - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - this.socket_!.send(JSON.stringify(invoke)); - } + /** + * Get state. + * + * Get current state of connection state with the websocket server. + * + * List of values are such like below: + * + * - `NONE`: The {@link WebConnector} instance is newly created, but did nothing yet. + * - `CONNECTING`: The {@link WebConnector.connect} method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The {@link WebConnector.close} method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): WebConnector.State { + return this.state_; + } - /** - * @hidden - */ - private _Handle_message(evt: MessageEvent): void { - if (typeof evt.data === "string") { - const invoke: Invoke = JSON.parse(evt.data); - this.replyData(invoke); - } + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + this.socket_!.send(JSON.stringify(invoke)); + } + + /** + * @hidden + */ + private _Handle_message(evt: MessageEvent): void { + if (typeof evt.data === "string") { + const invoke: Invoke = JSON.parse(evt.data); + this.replyData(invoke); } + } - /** - * @hidden - */ - private async _Handle_close(event: CloseEvent): Promise { - const error: WebError | undefined = - !event.code || event.code !== 1000 - ? new WebError(event.code, event.reason) - : undefined; + /** + * @hidden + */ + private async _Handle_close(event: CloseEvent): Promise { + const error: WebError | undefined = + !event.code || event.code !== 1000 + ? new WebError(event.code, event.reason) + : undefined; - this.state_ = WebConnector.State.CLOSED; - await this.destructor(error); - } + this.state_ = WebConnector.State.CLOSED; + await this.destructor(error); + } } /** * */ export namespace WebConnector { - /** - * Current state of the {@link WebConnector}. - */ - export import State = ConnectorBase.State; + /** + * Current state of the {@link WebConnector}. + */ + export import State = ConnectorBase.State; + /** + * Connection options for the {@link WebConnector.connect}. + */ + export interface IConnectOptions { /** - * Connection options for the {@link WebConnector.connect}. + * Milliseconds to wait the web-socket server to accept or reject it. If omitted, the waiting would be forever. */ - export interface IConnectOptions { - /** - * Milliseconds to wait the web-socket server to accept or reject it. If omitted, the waiting would be forever. - */ - timeout: number; - } + timeout: number; + } } diff --git a/src/protocols/web/WebError.ts b/src/protocols/web/WebError.ts index f0e14f1..22c7ee8 100644 --- a/src/protocols/web/WebError.ts +++ b/src/protocols/web/WebError.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- import { DomainError } from "tstl/exception/DomainError"; /** @@ -12,16 +7,16 @@ import { DomainError } from "tstl/exception/DomainError"; * @author Jeongho Nam - https://github.com/samchon */ export class WebError extends DomainError { - public readonly status: number; + public readonly status: number; - /** - * Initializer Constructor. - * - * @param status Status code. - * @param message Detailed message, the reaason why. - */ - public constructor(status: number, message: string) { - super(message); - this.status = status; - } + /** + * Initializer Constructor. + * + * @param status Status code. + * @param message Detailed message, the reaason why. + */ + public constructor(status: number, message: string) { + super(message); + this.status = status; + } } diff --git a/src/protocols/web/WebServer.ts b/src/protocols/web/WebServer.ts index d75e2cc..a7c2564 100644 --- a/src/protocols/web/WebServer.ts +++ b/src/protocols/web/WebServer.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- import type http from "http"; import type https from "https"; import type net from "net"; @@ -43,257 +38,238 @@ import { NodeModule } from "../../utils/internal/NodeModule"; * @author Jeongho Nam - https://github.com/samchon */ export class WebServer - implements IServer + implements IServer { - /** - * @hidden - */ - private state_: WebServer.State; - - /** - * @hidden - */ - private options_: https.ServerOptions | null; + /** + * @hidden + */ + private state_: WebServer.State; - /** - * @hidden - */ - private server_: http.Server | https.Server | null; + /** + * @hidden + */ + private options_: https.ServerOptions | null; - /** - * @hidden - */ - private protocol_: WebSocket.Server | null; + /** + * @hidden + */ + private server_: http.Server | https.Server | null; - /* ---------------------------------------------------------------- - CONSTRUCTORS - ---------------------------------------------------------------- */ - /** - * Default Constructor for the `ws` server.. - * - * Create an websocket server (`ws://`). - */ - public constructor(); + /** + * @hidden + */ + private protocol_: WebSocket.Server | null; - /** - * Initializer Constructor for the `wss` server. - * - * Create a secured websocket server (`wss://`). - * - * @param key Key string. - * @param cert Certification string. - */ - public constructor(key: string, cert: string); + /* ---------------------------------------------------------------- + CONSTRUCTORS + ---------------------------------------------------------------- */ + /** + * Default Constructor for the `ws` server.. + * + * Create an websocket server (`ws://`). + */ + public constructor(); - public constructor(key?: string, cert?: string) { - if (is_node() === false) - throw new DomainError( - "Error on WebServer.constructor(): only available in NodeJS.", - ); + /** + * Initializer Constructor for the `wss` server. + * + * Create a secured websocket server (`wss://`). + * + * @param key Key string. + * @param cert Certification string. + */ + public constructor(key: string, cert: string); - // PREPARE SREVER INSTANCE - this.options_ = !!key && !!cert ? { key, cert } : null; + public constructor(key?: string, cert?: string) { + if (is_node() === false) + throw new DomainError( + "Error on WebServer.constructor(): only available in NodeJS.", + ); - // INITIALIZE STATUS & PROTOCOL - this.state_ = WebServer.State.NONE; - this.server_ = null; - this.protocol_ = null; - } + // PREPARE SREVER INSTANCE + this.options_ = !!key && !!cert ? { key, cert } : null; - /** - * Open websocket server. - * - * Open a server through the web-socket protocol, with its *port* number and *handler* - * function determining whether to accept the client's connection or not. After the server has - * been opened, clients can connect to that websocket server by using the {@link WebConnector} - * class. - * - * When implementing the *handler* function with the {@link WebAcceptor} instance, calls the - * {@link WebAcceptor.accept} method if you want to accept the new client's connection. - * Otherwise you dont't want to accept the client and reject its connection, just calls the - * {@link WebAcceptor.reject} instead. - * - * @param port Port number to listen. - * @param handler Callback function for client connection. - */ - public async open( - port: number, - handler: (acceptor: WebAcceptor) => Promise, - ): Promise { - //---- - // PRELIMINARIES - //---- - // POSSIBLE TO OPEN? - if (this.state_ === WebServer.State.OPEN) - throw new DomainError( - "Error on WebServer.open(): it has already been opened.", - ); - else if (this.state_ === WebServer.State.OPENING) - throw new DomainError( - "Error on WebServer.open(): it's on opening, wait for a second.", - ); - else if (this.state_ === WebServer.State.CLOSING) - throw new RuntimeError( - "Error on WebServer.open(): it's on closing.", - ); - // DO OPEN - else if ( - this.server_ === null || - this.state_ === WebServer.State.CLOSED - ) - this.server_ = - this.options_ !== null - ? (await NodeModule.https.get()).createServer( - this.options_!, - ) - : (await NodeModule.http.get()).createServer(); - this.protocol_ = new (await NodeModule.ws.get()).default.Server({ - noServer: true, - }); + // INITIALIZE STATUS & PROTOCOL + this.state_ = WebServer.State.NONE; + this.server_ = null; + this.protocol_ = null; + } - // SET STATE - this.state_ = WebServer.State.OPENING; + /** + * Open websocket server. + * + * Open a server through the web-socket protocol, with its *port* number and *handler* + * function determining whether to accept the client's connection or not. After the server has + * been opened, clients can connect to that websocket server by using the {@link WebConnector} + * class. + * + * When implementing the *handler* function with the {@link WebAcceptor} instance, calls the + * {@link WebAcceptor.accept} method if you want to accept the new client's connection. + * Otherwise you dont't want to accept the client and reject its connection, just calls the + * {@link WebAcceptor.reject} instead. + * + * @param port Port number to listen. + * @param handler Callback function for client connection. + */ + public async open( + port: number, + handler: (acceptor: WebAcceptor) => Promise, + ): Promise { + //---- + // PRELIMINARIES + //---- + // POSSIBLE TO OPEN? + if (this.state_ === WebServer.State.OPEN) + throw new DomainError( + "Error on WebServer.open(): it has already been opened.", + ); + else if (this.state_ === WebServer.State.OPENING) + throw new DomainError( + "Error on WebServer.open(): it's on opening, wait for a second.", + ); + else if (this.state_ === WebServer.State.CLOSING) + throw new RuntimeError("Error on WebServer.open(): it's on closing."); + // DO OPEN + else if (this.server_ === null || this.state_ === WebServer.State.CLOSED) + this.server_ = + this.options_ !== null + ? (await NodeModule.https.get()).createServer(this.options_!) + : (await NodeModule.http.get()).createServer(); + this.protocol_ = new (await NodeModule.ws.get()).default.Server({ + noServer: true, + }); - //---- - // OPEN SERVER - //---- - // PROTOCOL - ADAPTOR & ACCEPTOR - this.server_.on( - "upgrade", - ( - request: http.IncomingMessage, - netSocket: net.Socket, - header: Buffer, - ) => { - this.protocol_!.handleUpgrade( - request, - netSocket, - header, - (webSocket) => { - webSocket.once( - "message", - async (data: WebSocket.Data) => { - // @todo: custom code is required - if (typeof data !== "string") webSocket.close(); + // SET STATE + this.state_ = WebServer.State.OPENING; - try { - const wrapper: IHeaderWrapper
= - JSON.parse(data as string); - const acceptor: WebAcceptor< - Header, - Provider - > = WebAcceptor.create( - request, - webSocket, - wrapper.header, - ); + //---- + // OPEN SERVER + //---- + // PROTOCOL - ADAPTOR & ACCEPTOR + this.server_.on( + "upgrade", + ( + request: http.IncomingMessage, + netSocket: net.Socket, + header: Buffer, + ) => { + this.protocol_!.handleUpgrade( + request, + netSocket, + header, + (webSocket) => { + webSocket.once("message", async (data: WebSocket.Data) => { + // @todo: custom code is required + if (typeof data !== "string") webSocket.close(); - await handler(acceptor); - } catch (exp) { - webSocket.close(); - } - }, - ); - }, + try { + const wrapper: IHeaderWrapper
= JSON.parse( + data as string, ); - }, - ); + const acceptor: WebAcceptor = + WebAcceptor.create(request, webSocket, wrapper.header); - // FINALIZATION - await WebServer._Open( - this.server_, - port, - (state) => (this.state_ = state), + await handler(acceptor); + } catch (exp) { + webSocket.close(); + } + }); + }, ); - } + }, + ); - /** - * Close server. - * - * Close all connections between its remote clients ({@link WebConnector}s). - * - * It destories all RFCs (remote function calls) between this server and remote clients - * (through `Driver`) that are not returned (completed) yet. The destruction - * causes all incompleted RFCs to throw exceptions. - */ - public async close(): Promise { - // VALIDATION - if (this.state_ !== WebServer.State.OPEN) - throw new DomainError( - "Error on WebServer.close(): server is not opened.", - ); + // FINALIZATION + await WebServer._Open(this.server_, port, (state) => (this.state_ = state)); + } - // DO CLOSE - this.state_ = WebServer.State.CLOSING; - await this._Close(); - this.state_ = WebServer.State.CLOSED; - } + /** + * Close server. + * + * Close all connections between its remote clients ({@link WebConnector}s). + * + * It destories all RFCs (remote function calls) between this server and remote clients + * (through `Driver`) that are not returned (completed) yet. The destruction + * causes all incompleted RFCs to throw exceptions. + */ + public async close(): Promise { + // VALIDATION + if (this.state_ !== WebServer.State.OPEN) + throw new DomainError( + "Error on WebServer.close(): server is not opened.", + ); - /** - * @hidden - */ - private static _Open( - server: http.Server | https.Server, - port: number, - setState: (state: WebServer.State) => void, - ): Promise { - return new Promise((resolve, reject) => { - // PREPARE RETURNS - server.on("listening", () => { - setState(WebServer.State.OPEN); - server.on("error", () => {}); - resolve(); - }); - server.on("error", (error) => { - setState(WebServer.State.NONE); - reject(error); - }); + // DO CLOSE + this.state_ = WebServer.State.CLOSING; + await this._Close(); + this.state_ = WebServer.State.CLOSED; + } - // DO OPEN - START PROVIDE - server.listen(port); - }); - } + /** + * @hidden + */ + private static _Open( + server: http.Server | https.Server, + port: number, + setState: (state: WebServer.State) => void, + ): Promise { + return new Promise((resolve, reject) => { + // PREPARE RETURNS + server.on("listening", () => { + setState(WebServer.State.OPEN); + server.on("error", () => {}); + resolve(); + }); + server.on("error", (error) => { + setState(WebServer.State.NONE); + reject(error); + }); - /** - * @hidden - */ - private _Close(): Promise { - return new Promise((resolve) => { - this.protocol_!.close(() => { - this.server_!.close(() => { - resolve(); - }); - }); + // DO OPEN - START PROVIDE + server.listen(port); + }); + } + + /** + * @hidden + */ + private _Close(): Promise { + return new Promise((resolve) => { + this.protocol_!.close(() => { + this.server_!.close(() => { + resolve(); }); - } + }); + }); + } - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Get server state. - * - * Get current state of the websocket server. - * - * List of values are such like below: - * - * - `NONE`: The `{@link WebServer} instance is newly created, but did nothing yet. - * - `OPENING`: The {@link WebServer.open} method is on running. - * - `OPEN`: The websocket server is online. - * - `CLOSING`: The {@link WebServer.close} method is on running. - * - `CLOSED`: The websocket server is offline. - */ - public get state(): WebServer.State { - return this.state_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Get server state. + * + * Get current state of the websocket server. + * + * List of values are such like below: + * + * - `NONE`: The `{@link WebServer} instance is newly created, but did nothing yet. + * - `OPENING`: The {@link WebServer.open} method is on running. + * - `OPEN`: The websocket server is online. + * - `CLOSING`: The {@link WebServer.close} method is on running. + * - `CLOSED`: The websocket server is offline. + */ + public get state(): WebServer.State { + return this.state_; + } } /** * */ export namespace WebServer { - /** - * Current state of the {@link WebServer}. - */ - export import State = IServer.State; + /** + * Current state of the {@link WebServer}. + */ + export import State = IServer.State; } diff --git a/src/protocols/web/index.ts b/src/protocols/web/index.ts index 9069f51..931283c 100644 --- a/src/protocols/web/index.ts +++ b/src/protocols/web/index.ts @@ -1,9 +1,4 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- -import * as web from "./module"; - -export default web; -export * from "./module"; +export * from "./WebServer"; +export * from "./WebAcceptor"; +export * from "./WebConnector"; +export * from "./WebError"; diff --git a/src/protocols/web/internal/IWebCommunicator.ts b/src/protocols/web/internal/IWebCommunicator.ts index 10e801d..4ce373c 100644 --- a/src/protocols/web/internal/IWebCommunicator.ts +++ b/src/protocols/web/internal/IWebCommunicator.ts @@ -1,28 +1,23 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- /** * Common interface for websocket communicators * * @author Jeongho Nam - https://github.com/samchon */ export interface IWebCommunicator { - /** - * Close connection. - * - * Close connection with the remote websocket system. - * - * It destories all RFCs (remote function calls) between this and remote websocket system - * (through `Driver`) that are not returned (completed) yet. The destruction - * causes all incompleted RFCs to throw exceptions. - * - * If parametric *code* and *reason* are specified, it means the disconnection is - * abnormal and it would throw special exceptions (`WebError`) to the incompleted RFCs. - * - * @param code Closing code. - * @param reason Reason why. - */ - close(code?: number, reason?: string): Promise; + /** + * Close connection. + * + * Close connection with the remote websocket system. + * + * It destories all RFCs (remote function calls) between this and remote websocket system + * (through `Driver`) that are not returned (completed) yet. The destruction + * causes all incompleted RFCs to throw exceptions. + * + * If parametric *code* and *reason* are specified, it means the disconnection is + * abnormal and it would throw special exceptions (`WebError`) to the incompleted RFCs. + * + * @param code Closing code. + * @param reason Reason why. + */ + close(code?: number, reason?: string): Promise; } diff --git a/src/protocols/web/internal/WebSocketPolyfill.ts b/src/protocols/web/internal/WebSocketPolyfill.ts index 5ebaffe..4e3931e 100644 --- a/src/protocols/web/internal/WebSocketPolyfill.ts +++ b/src/protocols/web/internal/WebSocketPolyfill.ts @@ -1,11 +1,6 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- import { NodeModule } from "../../../utils/internal/NodeModule"; export async function WebSocketPolyfill() { - const modulo = await NodeModule.ws.get(); - return modulo.default; + const modulo = await NodeModule.ws.get(); + return modulo.default; } diff --git a/src/protocols/web/module.ts b/src/protocols/web/module.ts deleted file mode 100644 index 7148456..0000000 --- a/src/protocols/web/module.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.web - */ -//---------------------------------------------------------------- -export * from "./WebServer"; -export * from "./WebAcceptor"; -export * from "./WebConnector"; -export * from "./WebError"; diff --git a/src/protocols/workers/SharedWorkerAcceptor.ts b/src/protocols/workers/SharedWorkerAcceptor.ts index 6477265..8158837 100644 --- a/src/protocols/workers/SharedWorkerAcceptor.ts +++ b/src/protocols/workers/SharedWorkerAcceptor.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { AcceptorBase } from "../internal/AcceptorBase"; import { IWorkerSystem } from "./internal/IWorkerSystem"; @@ -38,157 +33,157 @@ import { DomainError } from "tstl/exception/DomainError"; * @author Jeongho Nam - https://github.com/samchon */ export class SharedWorkerAcceptor - extends AcceptorBase - implements IWorkerSystem + extends AcceptorBase + implements IWorkerSystem { - /** - * @hidden - */ - private port_: MessagePort; - - /** - * @hidden - */ - private eraser_: () => void; - - /* ---------------------------------------------------------------- - CONSTRUCTOR - ---------------------------------------------------------------- */ - /** - * @internal - */ - public static create( - port: MessagePort, - header: Header, - eraser: () => void, - ): SharedWorkerAcceptor { - return new SharedWorkerAcceptor(port, header, eraser); - } - - /** - * @hidden - */ - private constructor(port: MessagePort, header: Header, eraser: () => void) { - super(header); - - // ASSIGN MEMBER - this.port_ = port; - this.eraser_ = eraser; - } - - /** - * @inheritDoc - */ - public async close(): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady("close"); - if (error) throw error; - - // CLOSE CONNECTION - this.state_ = SharedWorkerAcceptor.State.CLOSING; - await this._Close(); - } - - /** - * @hidden - */ - private async _Close(reason?: IReject): Promise { - // DESTRUCTOR - this.eraser_(); - await this.destructor(); - - // DO CLOSE - setTimeout(() => { - this.port_.postMessage( - reason === undefined - ? SharedWorkerAcceptor.State.CLOSING - : JSON.stringify(reason), - ); - this.port_.close(); - }); - - // WELL, IT MAY HARD TO SEE SUCH PROPERTIES - this.state_ = SharedWorkerAcceptor.State.CLOSED; - } - - /* ---------------------------------------------------------------- - HANDSHAKES - ---------------------------------------------------------------- */ - /** - * @inheritDoc - */ - public async accept(provider: Provider | null = null): Promise { - // TEST CONDITION - if (this.state_ !== SharedWorkerAcceptor.State.NONE) - throw new DomainError( - "Error on SharedWorkerAcceptor.accept(): you've already accepted (or rejected) the connection.", - ); - - //---- - // ACCEPT CONNECTION - //---- - this.state_ = SharedWorkerAcceptor.State.ACCEPTING; - { - // SET PROVIDER - this.provider_ = provider; - - // PREPARE PORT - this.port_.onmessage = this._Handle_message.bind(this); - this.port_.start(); - - // INFORM ACCEPTANCE - this.port_.postMessage(SharedWorkerAcceptor.State.OPEN); - } - this.state_ = SharedWorkerAcceptor.State.OPEN; - } - - /** - * Reject connection. - * - * Reject without acceptance, any interaction. The connection would be closed immediately. - * - * @param reason Detailed reason of the rejection. Default is "Rejected by server". - */ - public async reject(reason: string = "Rejected by server"): Promise { - // TEST CONDITION - if (this.state_ !== SharedWorkerAcceptor.State.NONE) - throw new DomainError( - "Error on SharedWorkerAcceptor.reject(): you've already accepted (or rejected) the connection.", - ); - - //---- - // REJECT CONNECTION (CLOSE) - //---- - this.state_ = SharedWorkerAcceptor.State.REJECTING; - await this._Close({ name: "reject", message: reason }); - } - - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - this.port_.postMessage(JSON.stringify(invoke)); - } - - /** - * @hidden - */ - private _Handle_message(evt: MessageEvent): void { - if (evt.data === SharedWorkerAcceptor.State.CLOSING) - this.close().catch(() => {}); - else this.replyData(JSON.parse(evt.data)); + /** + * @hidden + */ + private port_: MessagePort; + + /** + * @hidden + */ + private eraser_: () => void; + + /* ---------------------------------------------------------------- + CONSTRUCTOR + ---------------------------------------------------------------- */ + /** + * @internal + */ + public static create( + port: MessagePort, + header: Header, + eraser: () => void, + ): SharedWorkerAcceptor { + return new SharedWorkerAcceptor(port, header, eraser); + } + + /** + * @hidden + */ + private constructor(port: MessagePort, header: Header, eraser: () => void) { + super(header); + + // ASSIGN MEMBER + this.port_ = port; + this.eraser_ = eraser; + } + + /** + * @inheritDoc + */ + public async close(): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady("close"); + if (error) throw error; + + // CLOSE CONNECTION + this.state_ = SharedWorkerAcceptor.State.CLOSING; + await this._Close(); + } + + /** + * @hidden + */ + private async _Close(reason?: IReject): Promise { + // DESTRUCTOR + this.eraser_(); + await this.destructor(); + + // DO CLOSE + setTimeout(() => { + this.port_.postMessage( + reason === undefined + ? SharedWorkerAcceptor.State.CLOSING + : JSON.stringify(reason), + ); + this.port_.close(); + }); + + // WELL, IT MAY HARD TO SEE SUCH PROPERTIES + this.state_ = SharedWorkerAcceptor.State.CLOSED; + } + + /* ---------------------------------------------------------------- + HANDSHAKES + ---------------------------------------------------------------- */ + /** + * @inheritDoc + */ + public async accept(provider: Provider | null = null): Promise { + // TEST CONDITION + if (this.state_ !== SharedWorkerAcceptor.State.NONE) + throw new DomainError( + "Error on SharedWorkerAcceptor.accept(): you've already accepted (or rejected) the connection.", + ); + + //---- + // ACCEPT CONNECTION + //---- + this.state_ = SharedWorkerAcceptor.State.ACCEPTING; + { + // SET PROVIDER + this.provider_ = provider; + + // PREPARE PORT + this.port_.onmessage = this._Handle_message.bind(this); + this.port_.start(); + + // INFORM ACCEPTANCE + this.port_.postMessage(SharedWorkerAcceptor.State.OPEN); } + this.state_ = SharedWorkerAcceptor.State.OPEN; + } + + /** + * Reject connection. + * + * Reject without acceptance, any interaction. The connection would be closed immediately. + * + * @param reason Detailed reason of the rejection. Default is "Rejected by server". + */ + public async reject(reason: string = "Rejected by server"): Promise { + // TEST CONDITION + if (this.state_ !== SharedWorkerAcceptor.State.NONE) + throw new DomainError( + "Error on SharedWorkerAcceptor.reject(): you've already accepted (or rejected) the connection.", + ); + + //---- + // REJECT CONNECTION (CLOSE) + //---- + this.state_ = SharedWorkerAcceptor.State.REJECTING; + await this._Close({ name: "reject", message: reason }); + } + + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + this.port_.postMessage(JSON.stringify(invoke)); + } + + /** + * @hidden + */ + private _Handle_message(evt: MessageEvent): void { + if (evt.data === SharedWorkerAcceptor.State.CLOSING) + this.close().catch(() => {}); + else this.replyData(JSON.parse(evt.data)); + } } /** * */ export namespace SharedWorkerAcceptor { - /** - * Current state of the {@link SharedWorkerAcceptor}. - */ - export import State = AcceptorBase.State; + /** + * Current state of the {@link SharedWorkerAcceptor}. + */ + export import State = AcceptorBase.State; } diff --git a/src/protocols/workers/SharedWorkerConnector.ts b/src/protocols/workers/SharedWorkerConnector.ts index c1025cf..8b79b0d 100644 --- a/src/protocols/workers/SharedWorkerConnector.ts +++ b/src/protocols/workers/SharedWorkerConnector.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { ConnectorBase } from "../internal/ConnectorBase"; import { IWorkerSystem } from "./internal/IWorkerSystem"; @@ -52,238 +47,235 @@ import { sleep_until } from "tstl/thread/global"; * @author Jeongho Nam - https://github.com/samchon */ export class SharedWorkerConnector - extends ConnectorBase - implements IWorkerSystem + extends ConnectorBase + implements IWorkerSystem { - /** - * @hidden - */ - private port_?: MessagePort; + /** + * @hidden + */ + private port_?: MessagePort; - /* ---------------------------------------------------------------- - CONNECTIONS - ---------------------------------------------------------------- */ - /** - * Connect to remote server. - * - * The {@link connect}() method tries to connect an `SharedWorker` instance. If the - * `SharedWorker` instance is not created yet, the `SharedWorker` instance would be newly - * created. After the creation, the `SharedWorker` program must open that server using - * the {@link SharedWorkerServer.open}() method. - * - * After you business has been completed, you've to close the `SharedWorker` using one of - * them below. If you don't close that, vulnerable memory usage and communication channel - * would not be destroyed and it may cause the memory leak: - * - * - {@link close}() - * - {@link ShareDWorkerAcceptor.close}() - * - {@link SharedWorkerServer.close}() - * - * @param jsFile JS File to be {@link SharedWorkerServer}. - * @param options Detailed options like timeout. - */ - public async connect( - jsFile: string, - options: Partial = {}, - ): Promise { - // TEST CONDITION - if (this.port_ && this.state_ !== SharedWorkerConnector.State.CLOSED) { - if (this.state_ === SharedWorkerConnector.State.CONNECTING) - throw new DomainError("On connecting."); - else if (this.state_ === SharedWorkerConnector.State.OPEN) - throw new DomainError("Already connected."); - else throw new DomainError("Closing."); - } + /* ---------------------------------------------------------------- + CONNECTIONS + ---------------------------------------------------------------- */ + /** + * Connect to remote server. + * + * The {@link connect}() method tries to connect an `SharedWorker` instance. If the + * `SharedWorker` instance is not created yet, the `SharedWorker` instance would be newly + * created. After the creation, the `SharedWorker` program must open that server using + * the {@link SharedWorkerServer.open}() method. + * + * After you business has been completed, you've to close the `SharedWorker` using one of + * them below. If you don't close that, vulnerable memory usage and communication channel + * would not be destroyed and it may cause the memory leak: + * + * - {@link close}() + * - {@link ShareDWorkerAcceptor.close}() + * - {@link SharedWorkerServer.close}() + * + * @param jsFile JS File to be {@link SharedWorkerServer}. + * @param options Detailed options like timeout. + */ + public async connect( + jsFile: string, + options: Partial = {}, + ): Promise { + // TEST CONDITION + if (this.port_ && this.state_ !== SharedWorkerConnector.State.CLOSED) { + if (this.state_ === SharedWorkerConnector.State.CONNECTING) + throw new DomainError("On connecting."); + else if (this.state_ === SharedWorkerConnector.State.OPEN) + throw new DomainError("Already connected."); + else throw new DomainError("Closing."); + } - //---- - // CONNECTION - //---- - // TIME LIMIT - const at: Date | undefined = - options.timeout !== undefined - ? new Date(Date.now() + options.timeout) - : undefined; + //---- + // CONNECTION + //---- + // TIME LIMIT + const at: Date | undefined = + options.timeout !== undefined + ? new Date(Date.now() + options.timeout) + : undefined; - // SET CURRENT STATE - this.state_ = SharedWorkerConnector.State.CONNECTING; + // SET CURRENT STATE + this.state_ = SharedWorkerConnector.State.CONNECTING; - try { - // EXECUET THE WORKER - const worker: SharedWorker = new SharedWorker(jsFile); - this.port_ = worker.port as MessagePort; + try { + // EXECUET THE WORKER + const worker: SharedWorker = new SharedWorker(jsFile); + this.port_ = worker.port as MessagePort; - // WAIT THE WORKER TO BE READY - if ( - (await this._Handshake(options.timeout, at)) !== - SharedWorkerConnector.State.CONNECTING - ) - throw new DomainError( - `Error on SharedWorkerConnector.connect(): target shared-worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connecting.`, - ); + // WAIT THE WORKER TO BE READY + if ( + (await this._Handshake(options.timeout, at)) !== + SharedWorkerConnector.State.CONNECTING + ) + throw new DomainError( + `Error on SharedWorkerConnector.connect(): target shared-worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connecting.`, + ); - // SEND HEADERS - this.port_.postMessage( - JSON.stringify(IHeaderWrapper.wrap(this.header)), - ); + // SEND HEADERS + this.port_.postMessage(JSON.stringify(IHeaderWrapper.wrap(this.header))); - // WAIT ACCEPTION OR REJECTION - const last: string | SharedWorkerConnector.State.OPEN = - await this._Handshake(options.timeout, at); - if (last === SharedWorkerConnector.State.OPEN) { - // ACCEPTED - this.state_ = SharedWorkerConnector.State.OPEN; + // WAIT ACCEPTION OR REJECTION + const last: string | SharedWorkerConnector.State.OPEN = + await this._Handshake(options.timeout, at); + if (last === SharedWorkerConnector.State.OPEN) { + // ACCEPTED + this.state_ = SharedWorkerConnector.State.OPEN; - this.port_.onmessage = this._Handle_message.bind(this); - this.port_.onmessageerror = () => {}; - } else { - // REJECT OR HANDSHAKE ERROR - /* eslint-disable */ - let reject: IReject | null = null; - try { - reject = JSON.parse(last); - } catch {} + this.port_.onmessage = this._Handle_message.bind(this); + this.port_.onmessageerror = () => {}; + } else { + // REJECT OR HANDSHAKE ERROR + /* eslint-disable */ + let reject: IReject | null = null; + try { + reject = JSON.parse(last); + } catch {} - if ( - reject && - reject.name === "reject" && - typeof reject.message === "string" - ) - throw new RuntimeError(reject.message); - else - throw new DomainError( - `Error on SharedWorkerConnector.connect(): target shared-worker may not be opened by TGrid. It's not following the TGrid's own handshake rule.`, - ); - } - } catch (exp) { - try { - if (this.port_) this.port_.close(); - } catch {} + if ( + reject && + reject.name === "reject" && + typeof reject.message === "string" + ) + throw new RuntimeError(reject.message); + else + throw new DomainError( + `Error on SharedWorkerConnector.connect(): target shared-worker may not be opened by TGrid. It's not following the TGrid's own handshake rule.`, + ); + } + } catch (exp) { + try { + if (this.port_) this.port_.close(); + } catch {} - this.state_ = SharedWorkerConnector.State.NONE; - throw exp; - } + this.state_ = SharedWorkerConnector.State.NONE; + throw exp; } + } - /** - * @hidden - */ - private _Handshake(timeout?: number, at?: Date): Promise { - return new Promise((resolve, reject) => { - let completed: boolean = false; - let expired: boolean = false; - - if (at !== undefined) - sleep_until(at).then(() => { - if (completed === false) { - reject( - new DomainError( - `Error on SharedWorkerConnector.connect(): target shared-worker is not sending handshake data over ${timeout} milliseconds.`, - ), - ); - expired = true; - } - }); + /** + * @hidden + */ + private _Handshake(timeout?: number, at?: Date): Promise { + return new Promise((resolve, reject) => { + let completed: boolean = false; + let expired: boolean = false; - this.port_!.onmessage = once((evt) => { - if (expired === false) { - completed = true; - resolve(evt.data); - } - }); + if (at !== undefined) + sleep_until(at).then(() => { + if (completed === false) { + reject( + new DomainError( + `Error on SharedWorkerConnector.connect(): target shared-worker is not sending handshake data over ${timeout} milliseconds.`, + ), + ); + expired = true; + } }); - } - /** - * @inheritDoc - */ - public async close(): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady("close"); - if (error) throw error; + this.port_!.onmessage = once((evt) => { + if (expired === false) { + completed = true; + resolve(evt.data); + } + }); + }); + } - //---- - // CLOSE WITH JOIN - //---- - // PROMISE RETURN - const ret: Promise = this.join(); + /** + * @inheritDoc + */ + public async close(): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady("close"); + if (error) throw error; - // REQUEST CLOSE TO SERVER - this.state_ = SharedWorkerConnector.State.CLOSING; - this.port_!.postMessage(SharedWorkerConnector.State.CLOSING); + //---- + // CLOSE WITH JOIN + //---- + // PROMISE RETURN + const ret: Promise = this.join(); - // LAZY RETURN - await ret; - } + // REQUEST CLOSE TO SERVER + this.state_ = SharedWorkerConnector.State.CLOSING; + this.port_!.postMessage(SharedWorkerConnector.State.CLOSING); - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - this.port_!.postMessage(JSON.stringify(invoke)); - } + // LAZY RETURN + await ret; + } - /** - * @hidden - */ - private _Handle_message(evt: MessageEvent): void { - if (evt.data === SharedWorkerConnector.State.CLOSING) - this._Handle_close(); - // RFC OR REJECT - else { - const data: Invoke = JSON.parse(evt.data); - this.replyData(data as Invoke); - } - } + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + this.port_!.postMessage(JSON.stringify(invoke)); + } - /** - * @hidden - */ - private async _Handle_close(): Promise { - await this.destructor(); - this.state_ = SharedWorkerConnector.State.CLOSED; + /** + * @hidden + */ + private _Handle_message(evt: MessageEvent): void { + if (evt.data === SharedWorkerConnector.State.CLOSING) this._Handle_close(); + // RFC OR REJECT + else { + const data: Invoke = JSON.parse(evt.data); + this.replyData(data as Invoke); } + } + + /** + * @hidden + */ + private async _Handle_close(): Promise { + await this.destructor(); + this.state_ = SharedWorkerConnector.State.CLOSED; + } } /** * */ export namespace SharedWorkerConnector { - /** - * Current state of the {@link SharedWorkerConnector}. - */ - export import State = ConnectorBase.State; + /** + * Current state of the {@link SharedWorkerConnector}. + */ + export import State = ConnectorBase.State; + /** + * Connection options for the {@link SharedWorkerConnector.connect}. + */ + export interface IConnectOptions { /** - * Connection options for the {@link SharedWorkerConnector.connect}. + * Milliseconds to wait the shared-worker server to accept or reject it. If omitted, the waiting would be forever. */ - export interface IConnectOptions { - /** - * Milliseconds to wait the shared-worker server to accept or reject it. If omitted, the waiting would be forever. - */ - timeout: number; - } + timeout: number; + } - /** - * Compile JS source code. - * - * @param content Source code - * @return Temporary URL. - */ - export async function compile(content: string): Promise { - const { compile } = await WebWorkerCompiler(); - return compile(content); - } + /** + * Compile JS source code. + * + * @param content Source code + * @return Temporary URL. + */ + export async function compile(content: string): Promise { + const { compile } = await WebWorkerCompiler(); + return compile(content); + } - /** - * Remove compiled JS file. - * - * @param url Temporary URL. - */ - export async function remove(url: string): Promise { - const { remove } = await WebWorkerCompiler(); - await remove(url); - } + /** + * Remove compiled JS file. + * + * @param url Temporary URL. + */ + export async function remove(url: string): Promise { + const { remove } = await WebWorkerCompiler(); + await remove(url); + } } diff --git a/src/protocols/workers/SharedWorkerServer.ts b/src/protocols/workers/SharedWorkerServer.ts index 2b65020..a779feb 100644 --- a/src/protocols/workers/SharedWorkerServer.ts +++ b/src/protocols/workers/SharedWorkerServer.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { IServer } from "../internal/IServer"; import { SharedWorkerAcceptor } from "./SharedWorkerAcceptor"; @@ -44,149 +39,149 @@ import { is_node } from "tstl/utility/node"; * @author Jeongho Nam - https://github.com/samchon */ export class SharedWorkerServer - implements IServer + implements IServer { - /** - * @hidden - */ - private state_: SharedWorkerServer.State; + /** + * @hidden + */ + private state_: SharedWorkerServer.State; - /** - * @hidden - */ - private acceptors_: HashSet>; + /** + * @hidden + */ + private acceptors_: HashSet>; - /* ---------------------------------------------------------------- - CONSTRUCTOR - ---------------------------------------------------------------- */ - /** - * Default Constructor. - */ - public constructor() { - this.acceptors_ = new HashSet(); - this.state_ = SharedWorkerServer.State.NONE; - } + /* ---------------------------------------------------------------- + CONSTRUCTOR + ---------------------------------------------------------------- */ + /** + * Default Constructor. + */ + public constructor() { + this.acceptors_ = new HashSet(); + this.state_ = SharedWorkerServer.State.NONE; + } - /** - * Open shared worker server. - * - * Open a server through the shared worker protocol, with *handler* function determining - * whether to accept the client's connection or not. After the server has been opened, clients - * can connect to that websocket server by using the {@link SharedWorkerServer} class. - * - * When implementing the *handler* function with the {@link SharedWorkerServer} instance, calls - * the {@link SharedWorkerAcceptor.accept} method if you want to accept the new client's - * connection. Otherwise you dont't want to accept the client and reject its connection, just - * calls the {@link SharedWorkerAcceptor.reject} instead. - * - * @param handler Callback function called whenever client connects. - */ - public async open( - handler: ( - acceptor: SharedWorkerAcceptor, - ) => Promise, - ): Promise { - // TEST CONDITION - if (is_node() === true) - throw new DomainError( - "Error on SharedWorkerServer.open(): SharedWorker is not supported in the NodeJS.", - ); - else if (self.document !== undefined) - throw new DomainError( - "Error on SharedWorkerServer.open(): this is not the SharedWorker.", - ); - else if (this.state_ !== SharedWorkerServer.State.NONE) - throw new DomainError( - "Error on SharedWorkerServer.open(): the server has been opened yet.", - ); + /** + * Open shared worker server. + * + * Open a server through the shared worker protocol, with *handler* function determining + * whether to accept the client's connection or not. After the server has been opened, clients + * can connect to that websocket server by using the {@link SharedWorkerServer} class. + * + * When implementing the *handler* function with the {@link SharedWorkerServer} instance, calls + * the {@link SharedWorkerAcceptor.accept} method if you want to accept the new client's + * connection. Otherwise you dont't want to accept the client and reject its connection, just + * calls the {@link SharedWorkerAcceptor.reject} instead. + * + * @param handler Callback function called whenever client connects. + */ + public async open( + handler: ( + acceptor: SharedWorkerAcceptor, + ) => Promise, + ): Promise { + // TEST CONDITION + if (is_node() === true) + throw new DomainError( + "Error on SharedWorkerServer.open(): SharedWorker is not supported in the NodeJS.", + ); + else if (self.document !== undefined) + throw new DomainError( + "Error on SharedWorkerServer.open(): this is not the SharedWorker.", + ); + else if (this.state_ !== SharedWorkerServer.State.NONE) + throw new DomainError( + "Error on SharedWorkerServer.open(): the server has been opened yet.", + ); - //---- - // OPE SHARED-WORKER - //---- - this.state_ = SharedWorkerServer.State.OPENING; - { - self.addEventListener("connect", (evt) => { - for (const port of (evt as OpenEvent).ports) - this._Handle_connect(port, handler); - }); - } - this.state_ = SharedWorkerServer.State.OPEN; + //---- + // OPE SHARED-WORKER + //---- + this.state_ = SharedWorkerServer.State.OPENING; + { + self.addEventListener("connect", (evt) => { + for (const port of (evt as OpenEvent).ports) + this._Handle_connect(port, handler); + }); } + this.state_ = SharedWorkerServer.State.OPEN; + } - /** - * Close server. - * - * Close all connections between its remote clients ({@link SharedWorkerConnector}s). - * - * It destories all RFCs (remote function calls) between this server and remote clients - * (through `Driver`) that are not returned (completed) yet. The destruction - * causes all incompleted RFCs to throw exceptions. - */ - public async close(): Promise { - // TEST VALIDATION - if (this.state_ !== SharedWorkerServer.State.OPEN) - throw new DomainError( - "Error on SharedWorkerServer.close(): the server is not opened.", - ); + /** + * Close server. + * + * Close all connections between its remote clients ({@link SharedWorkerConnector}s). + * + * It destories all RFCs (remote function calls) between this server and remote clients + * (through `Driver`) that are not returned (completed) yet. The destruction + * causes all incompleted RFCs to throw exceptions. + */ + public async close(): Promise { + // TEST VALIDATION + if (this.state_ !== SharedWorkerServer.State.OPEN) + throw new DomainError( + "Error on SharedWorkerServer.close(): the server is not opened.", + ); - // CLOSE ALL CONNECTIONS - for (const acceptor of this.acceptors_) await acceptor.close(); - } + // CLOSE ALL CONNECTIONS + for (const acceptor of this.acceptors_) await acceptor.close(); + } - /** - * @hidden - */ - private _Handle_connect( - port: MessagePort, - handler: (acceptor: SharedWorkerAcceptor) => any, - ): void { - port.onmessage = once((evt) => { - // ARGUMENTS - const wrapper: IHeaderWrapper
= JSON.parse(evt.data); + /** + * @hidden + */ + private _Handle_connect( + port: MessagePort, + handler: (acceptor: SharedWorkerAcceptor) => any, + ): void { + port.onmessage = once((evt) => { + // ARGUMENTS + const wrapper: IHeaderWrapper
= JSON.parse(evt.data); - // CREATE ACCEPTOR - /* eslint-disable */ - let acceptor: SharedWorkerAcceptor; - acceptor = SharedWorkerAcceptor.create(port, wrapper.header, () => { - this.acceptors_.erase(acceptor); - }); - this.acceptors_.insert(acceptor); + // CREATE ACCEPTOR + /* eslint-disable */ + let acceptor: SharedWorkerAcceptor; + acceptor = SharedWorkerAcceptor.create(port, wrapper.header, () => { + this.acceptors_.erase(acceptor); + }); + this.acceptors_.insert(acceptor); - // SHIFT TO THE CALLBACK - handler(acceptor); - }); - port.postMessage(SharedWorkerServer.State.OPENING); - } + // SHIFT TO THE CALLBACK + handler(acceptor); + }); + port.postMessage(SharedWorkerServer.State.OPENING); + } - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * Get server state. - * - * Get current state of the websocket server. - * - * List of values are such like below: - * - * - `NONE`: The `{@link SharedWorkerServer} instance is newly created, but did nothing yet. - * - `OPENING`: The {@link SharedWorkerServer.open} method is on running. - * - `OPEN`: The websocket server is online. - * - `CLOSING`: The {@link SharedWorkerServer.close} method is on running. - * - `CLOSED`: The websocket server is offline. - */ - public get state(): SharedWorkerServer.State { - return this.state_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * Get server state. + * + * Get current state of the websocket server. + * + * List of values are such like below: + * + * - `NONE`: The `{@link SharedWorkerServer} instance is newly created, but did nothing yet. + * - `OPENING`: The {@link SharedWorkerServer.open} method is on running. + * - `OPEN`: The websocket server is online. + * - `CLOSING`: The {@link SharedWorkerServer.close} method is on running. + * - `CLOSED`: The websocket server is offline. + */ + public get state(): SharedWorkerServer.State { + return this.state_; + } } /** * */ export namespace SharedWorkerServer { - /** - * Current state of the {@link SharedWorkerServer}. - */ - export import State = IServer.State; + /** + * Current state of the {@link SharedWorkerServer}. + */ + export import State = IServer.State; } /** diff --git a/src/protocols/workers/WorkerConnector.ts b/src/protocols/workers/WorkerConnector.ts index 2c11c17..182570d 100644 --- a/src/protocols/workers/WorkerConnector.ts +++ b/src/protocols/workers/WorkerConnector.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { DomainError } from "tstl/exception/DomainError"; import { is_node } from "tstl/utility/node"; import { sleep_until } from "tstl/thread/global"; @@ -47,301 +42,299 @@ import { Singleton } from "tstl/thread/Singleton"; * @author Jeongho Nam - https://github.com/samchon */ export class WorkerConnector - extends ConnectorBase - implements IWorkerSystem + extends ConnectorBase + implements IWorkerSystem { - private readonly compiler_: Singleton>; - - /** - * @hidden - */ - private worker_?: Worker; + private readonly compiler_: Singleton>; + + /** + * @hidden + */ + private worker_?: Worker; + + /** + * Initializer Constructor. + * + * @param header An object containing initialization data like activation. + * @param provider An object providing features for remote system. + * @param type You can specify the worker mode when NodeJS. Default is "thread". + */ + public constructor( + header: Header, + provider: Provider, + type: "thread" | "process" = "thread", + ) { + super(header, provider); + this.compiler_ = new Singleton(() => + is_node() ? NodeWorkerCompiler(type) : WebWorkerCompiler(), + ); + } + + /* ---------------------------------------------------------------- + CONNECTIONS + ---------------------------------------------------------------- */ + /** + * Compile server and connect to there. + * + * The {@link compile} method tries compile JS source code, creates `Worker` instance + * with that code connects to the `Worker`. To complete the compilation and connection, + * the `Worker` program must open that server using the {@link WorkerServer.open}() + * method. + * + * Note that, after your business has been completed, you've to close the `Worker` using + * {@link close}() or {@link WorkerServer.close}(). If you don't close that, vulnerable + * memory usage and communication channel would not be destroyed and it may cause the + * memory leak. + * + * @param content JS Source code to compile. + * @param timeout Detailed options like timeout. + */ + public async compile( + content: string, + options: Partial = {}, + ): Promise { + //---- + // PRELIMINIARIES + //---- + // TEST CONDITION + this._Test_connection("compile"); + + // COMPILATION + const compiler: IWorkerCompiler = await this.compiler_.get(); + const path: string = await compiler.compile(content); + let error: Error | null = null; // FOR LAZY-THROWING + + //---- + // CONNECT + //---- + // TRY CONNECTION + try { + await this._Connect("compile", path, options); + } catch (exp) { + error = exp as Error; + } - /** - * Initializer Constructor. - * - * @param header An object containing initialization data like activation. - * @param provider An object providing features for remote system. - * @param type You can specify the worker mode when NodeJS. Default is "thread". - */ - public constructor( - header: Header, - provider: Provider, - type: "thread" | "process" = "thread", - ) { - super(header, provider); - this.compiler_ = new Singleton(() => - is_node() ? NodeWorkerCompiler(type) : WebWorkerCompiler(), + // REMOVE THE TEMPORARY FILE + await compiler.remove(path); + + // LAZY THROWING + if (error !== null) throw error; + } + + /** + * Connect to server. + * + * The {@link connect}() method tries to create an `Worker` instance and connect to the + * `Worker`. To complete the connection, the `Worker` program must open that server using + * the {@link WorkerServer.open}() method. + * + * Note that, after your business has been completed, you've to close the `Worker` using + * {@link close}() or {@link WorkerServer.close}(). If you don't close that, vulnerable + * memory usage and communication channel would not be destroyed and it may cause the + * memory leak. + * + * @param jsFile JS File to be {@link WorkerServer}. + * @param args Headers containing initialization data like activation. + * @param timeout Detailed options like timeout. + */ + public async connect( + jsFile: string, + options: Partial = {}, + ): Promise { + // TEST CONDITION + this._Test_connection("connect"); + + // DO CONNECT + await this._Connect("connect", jsFile, options); + } + + /** + * @hidden + */ + private _Test_connection(method: string): void { + if (this.worker_ && this.state !== WorkerConnector.State.CLOSED) { + if (this.state_ === WorkerConnector.State.CONNECTING) + throw new DomainError( + `Error on WorkerConnector.${method}(): on connecting.`, + ); + else if (this.state_ === WorkerConnector.State.OPEN) + throw new DomainError( + `Error on WorkerConnector.${method}(): already connected.`, ); + else + throw new DomainError(`Error on WorkerConnector.${method}(): closing.`); } + } + + /** + * @hidden + */ + private async _Connect( + method: string, + jsFile: string, + options: Partial, + ): Promise { + // TIME LIMIT + const at: Date | undefined = + options.timeout !== undefined + ? new Date(Date.now() + options.timeout) + : undefined; + + // SET CURRENT STATE + this.state_ = WorkerConnector.State.CONNECTING; + + try { + // EXECUTE THE WORKER + const compiler: IWorkerCompiler = await this.compiler_.get(); + this.worker_ = await compiler.execute( + jsFile, + is_node() === true ? options.execArgv : undefined, + ); + + // WAIT THE WORKER TO BE READY + if ( + (await this._Handshake(method, options.timeout, at)) !== + WorkerConnector.State.CONNECTING + ) + throw new DomainError( + `Error on WorkerConnector.${method}(): target worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connecting.`, + ); - /* ---------------------------------------------------------------- - CONNECTIONS - ---------------------------------------------------------------- */ - /** - * Compile server and connect to there. - * - * The {@link compile} method tries compile JS source code, creates `Worker` instance - * with that code connects to the `Worker`. To complete the compilation and connection, - * the `Worker` program must open that server using the {@link WorkerServer.open}() - * method. - * - * Note that, after your business has been completed, you've to close the `Worker` using - * {@link close}() or {@link WorkerServer.close}(). If you don't close that, vulnerable - * memory usage and communication channel would not be destroyed and it may cause the - * memory leak. - * - * @param content JS Source code to compile. - * @param timeout Detailed options like timeout. - */ - public async compile( - content: string, - options: Partial = {}, - ): Promise { - //---- - // PRELIMINIARIES - //---- - // TEST CONDITION - this._Test_connection("compile"); - - // COMPILATION - const compiler: IWorkerCompiler = await this.compiler_.get(); - const path: string = await compiler.compile(content); - let error: Error | null = null; // FOR LAZY-THROWING - - //---- - // CONNECT - //---- - // TRY CONNECTION - try { - await this._Connect("compile", path, options); - } catch (exp) { - error = exp as Error; - } - - // REMOVE THE TEMPORARY FILE - await compiler.remove(path); + // SEND HEADERS + this.worker_!.postMessage( + JSON.stringify(IHeaderWrapper.wrap(this.header)), + ); + + // WAIT COMPLETION + if ( + (await this._Handshake(method, options.timeout, at)) !== + WorkerConnector.State.OPEN + ) + throw new DomainError( + `Error on WorkerConnector.${method}(): target worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connected.`, + ); - // LAZY THROWING - if (error !== null) throw error; - } + // SUCCESS + this.state_ = WorkerConnector.State.OPEN; + this.worker_!.onmessage = this._Handle_message.bind(this); + } catch (exp) { + try { + if (this.worker_) this.worker_.terminate(); + } catch {} - /** - * Connect to server. - * - * The {@link connect}() method tries to create an `Worker` instance and connect to the - * `Worker`. To complete the connection, the `Worker` program must open that server using - * the {@link WorkerServer.open}() method. - * - * Note that, after your business has been completed, you've to close the `Worker` using - * {@link close}() or {@link WorkerServer.close}(). If you don't close that, vulnerable - * memory usage and communication channel would not be destroyed and it may cause the - * memory leak. - * - * @param jsFile JS File to be {@link WorkerServer}. - * @param args Headers containing initialization data like activation. - * @param timeout Detailed options like timeout. - */ - public async connect( - jsFile: string, - options: Partial = {}, - ): Promise { - // TEST CONDITION - this._Test_connection("connect"); - - // DO CONNECT - await this._Connect("connect", jsFile, options); + this.state_ = WorkerConnector.State.NONE; + throw exp; } - - /** - * @hidden - */ - private _Test_connection(method: string): void { - if (this.worker_ && this.state !== WorkerConnector.State.CLOSED) { - if (this.state_ === WorkerConnector.State.CONNECTING) - throw new DomainError( - `Error on WorkerConnector.${method}(): on connecting.`, - ); - else if (this.state_ === WorkerConnector.State.OPEN) - throw new DomainError( - `Error on WorkerConnector.${method}(): already connected.`, - ); - else - throw new DomainError( - `Error on WorkerConnector.${method}(): closing.`, - ); - } - } - - /** - * @hidden - */ - private async _Connect( - method: string, - jsFile: string, - options: Partial, - ): Promise { - // TIME LIMIT - const at: Date | undefined = - options.timeout !== undefined - ? new Date(Date.now() + options.timeout) - : undefined; - - // SET CURRENT STATE - this.state_ = WorkerConnector.State.CONNECTING; - - try { - // EXECUTE THE WORKER - const compiler: IWorkerCompiler = await this.compiler_.get(); - this.worker_ = await compiler.execute( - jsFile, - is_node() === true ? options.execArgv : undefined, + } + + /** + * @hidden + */ + private _Handshake( + method: string, + timeout?: number, + until?: Date, + ): Promise { + return new Promise((resolve, reject) => { + /* eslint-disable */ + let completed: boolean = false; + + /* eslint-disable */ + let expired: boolean = false; + + if (until !== undefined) + sleep_until(until).then(() => { + if (completed === false) { + reject( + new DomainError( + `Error on WorkerConnector.${method}(): target worker is not sending handshake data over ${timeout} milliseconds.`, + ), ); - - // WAIT THE WORKER TO BE READY - if ( - (await this._Handshake(method, options.timeout, at)) !== - WorkerConnector.State.CONNECTING - ) - throw new DomainError( - `Error on WorkerConnector.${method}(): target worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connecting.`, - ); - - // SEND HEADERS - this.worker_!.postMessage( - JSON.stringify(IHeaderWrapper.wrap(this.header)), - ); - - // WAIT COMPLETION - if ( - (await this._Handshake(method, options.timeout, at)) !== - WorkerConnector.State.OPEN - ) - throw new DomainError( - `Error on WorkerConnector.${method}(): target worker may not be opened by TGrid. It's not following the TGrid's own handshake rule when connected.`, - ); - - // SUCCESS - this.state_ = WorkerConnector.State.OPEN; - this.worker_!.onmessage = this._Handle_message.bind(this); - } catch (exp) { - try { - if (this.worker_) this.worker_.terminate(); - } catch {} - - this.state_ = WorkerConnector.State.NONE; - throw exp; - } - } - - /** - * @hidden - */ - private _Handshake( - method: string, - timeout?: number, - until?: Date, - ): Promise { - return new Promise((resolve, reject) => { - /* eslint-disable */ - let completed: boolean = false; - - /* eslint-disable */ - let expired: boolean = false; - - if (until !== undefined) - sleep_until(until).then(() => { - if (completed === false) { - reject( - new DomainError( - `Error on WorkerConnector.${method}(): target worker is not sending handshake data over ${timeout} milliseconds.`, - ), - ); - expired = true; - } - }); - - this.worker_!.onmessage = once((evt) => { - if (expired === false) { - completed = true; - resolve(evt.data); - } - }); + expired = true; + } }); - } - - /** - * @inheritDoc - */ - public async close(): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady("close"); - if (error) throw error; - - //---- - // CLOSE WITH JOIN - //---- - // PROMISE RETURN - const ret: Promise = this.join(); - - // REQUEST CLOSE TO SERVER - this.state_ = WorkerConnector.State.CLOSING; - this.worker_!.postMessage(WorkerConnector.State.CLOSING); - - // LAZY RETURN - await ret; - } - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - this.worker_!.postMessage(JSON.stringify(invoke)); - } - - /** - * @hidden - */ - private _Handle_message(evt: MessageEvent): void { - if (evt.data === WorkerConnector.State.CLOSING) - this._Handle_close().catch(() => {}); - else this.replyData(JSON.parse(evt.data)); - } - - /** - * @hidden - */ - private async _Handle_close(): Promise { - // STATE & PROMISE RETURN - await this.destructor(); - this.state_ = WorkerConnector.State.CLOSED; - } + this.worker_!.onmessage = once((evt) => { + if (expired === false) { + completed = true; + resolve(evt.data); + } + }); + }); + } + + /** + * @inheritDoc + */ + public async close(): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady("close"); + if (error) throw error; + + //---- + // CLOSE WITH JOIN + //---- + // PROMISE RETURN + const ret: Promise = this.join(); + + // REQUEST CLOSE TO SERVER + this.state_ = WorkerConnector.State.CLOSING; + this.worker_!.postMessage(WorkerConnector.State.CLOSING); + + // LAZY RETURN + await ret; + } + + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + this.worker_!.postMessage(JSON.stringify(invoke)); + } + + /** + * @hidden + */ + private _Handle_message(evt: MessageEvent): void { + if (evt.data === WorkerConnector.State.CLOSING) + this._Handle_close().catch(() => {}); + else this.replyData(JSON.parse(evt.data)); + } + + /** + * @hidden + */ + private async _Handle_close(): Promise { + // STATE & PROMISE RETURN + await this.destructor(); + this.state_ = WorkerConnector.State.CLOSED; + } } /** * */ export namespace WorkerConnector { + /** + * Current state of the {@link WorkerConnector}. + */ + export import State = ConnectorBase.State; + + /** + * Connection options for the {@link WorkerConnector.connect}. + */ + export interface IConnectOptions { /** - * Current state of the {@link WorkerConnector}. + * Milliseconds to wait the worker server to accept or reject it. If omitted, the waiting would be forever. */ - export import State = ConnectorBase.State; + timeout: number; /** - * Connection options for the {@link WorkerConnector.connect}. + * Arguments only for the NodeJS environments. */ - export interface IConnectOptions { - /** - * Milliseconds to wait the worker server to accept or reject it. If omitted, the waiting would be forever. - */ - timeout: number; - - /** - * Arguments only for the NodeJS environments. - */ - execArgv: string[]; - } + execArgv: string[]; + } } diff --git a/src/protocols/workers/WorkerServer.ts b/src/protocols/workers/WorkerServer.ts index 6ac51ae..f0a490d 100644 --- a/src/protocols/workers/WorkerServer.ts +++ b/src/protocols/workers/WorkerServer.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { DomainError } from "tstl/exception/DomainError"; import { RuntimeError } from "tstl/exception/RuntimeError"; import { Singleton } from "tstl/thread/Singleton"; @@ -50,224 +45,224 @@ import { ProcessChannel } from "./internal/processes/ProcessChannel"; * @author Jeongho Nam - https://github.com/samchon */ export class WorkerServer - extends Communicator - implements IWorkerSystem, IServer + extends Communicator + implements IWorkerSystem, IServer { - /** - * @hidden - */ - private channel_: Singleton>; + /** + * @hidden + */ + private channel_: Singleton>; - /** - * @hidden - */ - private state_: WorkerServer.State; + /** + * @hidden + */ + private state_: WorkerServer.State; - /** - * @hidden - */ - private header_: Singleton>; + /** + * @hidden + */ + private header_: Singleton>; - /* ---------------------------------------------------------------- - CONSTRUCTOR - ---------------------------------------------------------------- */ - /** - * Default Constructor. - * - * @param type You can specify the worker mode when NodeJS. Default is "thread". - */ - public constructor() { - super(undefined); + /* ---------------------------------------------------------------- + CONSTRUCTOR + ---------------------------------------------------------------- */ + /** + * Default Constructor. + * + * @param type You can specify the worker mode when NodeJS. Default is "thread". + */ + public constructor() { + super(undefined); - this.channel_ = new Singleton(async () => { - if (is_node() === false) return (self) as IFeature; + this.channel_ = new Singleton(async () => { + if (is_node() === false) return (self) as IFeature; - const threadPort = await ThreadPort(); - return threadPort.is_worker_server() - ? ((threadPort) as IFeature) - : (ProcessChannel as IFeature); - }); - this.state_ = WorkerServer.State.NONE; - this.header_ = new Singleton(async () => { - (await this.channel_.get()).postMessage(WorkerServer.State.OPENING); + const threadPort = await ThreadPort(); + return threadPort.is_worker_server() + ? ((threadPort) as IFeature) + : (ProcessChannel as IFeature); + }); + this.state_ = WorkerServer.State.NONE; + this.header_ = new Singleton(async () => { + (await this.channel_.get()).postMessage(WorkerServer.State.OPENING); - const data: string = await this._Handshake("getHeader"); - const wrapper: IHeaderWrapper
= JSON.parse(data); + const data: string = await this._Handshake("getHeader"); + const wrapper: IHeaderWrapper
= JSON.parse(data); - return wrapper.header; - }); - } + return wrapper.header; + }); + } - /** - * Open server with `Provider`. - * - * Open worker server and start communication with the remote system - * ({@link WorkerConnector}). - * - * Note that, after your business, you should terminate this worker to prevent waste - * of memory leak. Close this worker by yourself ({@link close}) or let remote client to - * close this worker ({@link WorkerConnector.close}). - * - * @param provider An object providing featrues for the remote system. - */ - public async open(provider: Provider): Promise { - // TEST CONDITION - if (is_node() === false) { - if (self.document !== undefined) - throw new DomainError( - "Error on WorkerServer.open(): this is not Worker.", - ); - } else if ((await this.channel_.get()).is_worker_server() === false) - throw new DomainError( - "Error on WorkerServer.open(): this is not Worker.", - ); - else if (this.state_ !== WorkerServer.State.NONE) - throw new DomainError( - "Error on WorkerServer.open(): the server has been opened yet.", - ); + /** + * Open server with `Provider`. + * + * Open worker server and start communication with the remote system + * ({@link WorkerConnector}). + * + * Note that, after your business, you should terminate this worker to prevent waste + * of memory leak. Close this worker by yourself ({@link close}) or let remote client to + * close this worker ({@link WorkerConnector.close}). + * + * @param provider An object providing featrues for the remote system. + */ + public async open(provider: Provider): Promise { + // TEST CONDITION + if (is_node() === false) { + if (self.document !== undefined) + throw new DomainError( + "Error on WorkerServer.open(): this is not Worker.", + ); + } else if ((await this.channel_.get()).is_worker_server() === false) + throw new DomainError( + "Error on WorkerServer.open(): this is not Worker.", + ); + else if (this.state_ !== WorkerServer.State.NONE) + throw new DomainError( + "Error on WorkerServer.open(): the server has been opened yet.", + ); - // OPEN WORKER - this.state_ = WorkerServer.State.OPENING; - this.provider_ = provider; + // OPEN WORKER + this.state_ = WorkerServer.State.OPENING; + this.provider_ = provider; - // GET HEADERS - await this.header_.get(); + // GET HEADERS + await this.header_.get(); - // SUCCESS - const channel = await this.channel_.get(); - channel.onmessage = this._Handle_message.bind(this); - channel.postMessage(WorkerServer.State.OPEN); + // SUCCESS + const channel = await this.channel_.get(); + channel.onmessage = this._Handle_message.bind(this); + channel.postMessage(WorkerServer.State.OPEN); - this.state_ = WorkerServer.State.OPEN; - } + this.state_ = WorkerServer.State.OPEN; + } - /** - * @inheritDoc - */ - public async close(): Promise { - // TEST CONDITION - const error: Error | null = this.inspectReady(); - if (error) throw error; + /** + * @inheritDoc + */ + public async close(): Promise { + // TEST CONDITION + const error: Error | null = this.inspectReady(); + if (error) throw error; - //---- - // CLOSE WORKER - //---- - this.state_ = WorkerServer.State.CLOSING; - { - // HANDLERS - await this.destructor(); + //---- + // CLOSE WORKER + //---- + this.state_ = WorkerServer.State.CLOSING; + { + // HANDLERS + await this.destructor(); - // DO CLOSE - setTimeout(async () => { - const channel = await this.channel_.get(); - channel.postMessage(WorkerServer.State.CLOSING); - channel.close(); - }); - } - this.state_ = WorkerServer.State.CLOSED; + // DO CLOSE + setTimeout(async () => { + const channel = await this.channel_.get(); + channel.postMessage(WorkerServer.State.CLOSING); + channel.close(); + }); } + this.state_ = WorkerServer.State.CLOSED; + } - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - /** - * @inheritDoc - */ - public get state(): WorkerServer.State { - return this.state_; - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + /** + * @inheritDoc + */ + public get state(): WorkerServer.State { + return this.state_; + } - /** - * Get header containing initialization data like activation. - */ - public getHeader(): Promise
{ - return this.header_.get(); - } + /** + * Get header containing initialization data like activation. + */ + public getHeader(): Promise
{ + return this.header_.get(); + } - /** - * @hidden - */ - private _Handshake( - method: string, - timeout?: number, - until?: Date, - ): Promise { - return new Promise(async (resolve, reject) => { - /* eslint-disable */ - let completed: boolean = false; + /** + * @hidden + */ + private _Handshake( + method: string, + timeout?: number, + until?: Date, + ): Promise { + return new Promise(async (resolve, reject) => { + /* eslint-disable */ + let completed: boolean = false; - /* eslint-disable */ - let expired: boolean = false; + /* eslint-disable */ + let expired: boolean = false; - if (until !== undefined) - sleep_until(until) - .then(() => { - if (completed === false) { - reject( - new DomainError( - `Error on WorkerConnector.${method}(): target worker is not sending handshake data over ${timeout} milliseconds.`, - ), - ); - expired = true; - } - }) - .catch(() => {}); + if (until !== undefined) + sleep_until(until) + .then(() => { + if (completed === false) { + reject( + new DomainError( + `Error on WorkerConnector.${method}(): target worker is not sending handshake data over ${timeout} milliseconds.`, + ), + ); + expired = true; + } + }) + .catch(() => {}); - (await this.channel_.get()).onmessage = once((evt) => { - if (expired === false) { - completed = true; - resolve(evt.data); - } - }); - }); - } + (await this.channel_.get()).onmessage = once((evt) => { + if (expired === false) { + completed = true; + resolve(evt.data); + } + }); + }); + } - /* ---------------------------------------------------------------- - COMMUNICATOR - ---------------------------------------------------------------- */ - /** - * @hidden - */ - protected async sendData(invoke: Invoke): Promise { - (await this.channel_.get()).postMessage(JSON.stringify(invoke)); - } + /* ---------------------------------------------------------------- + COMMUNICATOR + ---------------------------------------------------------------- */ + /** + * @hidden + */ + protected async sendData(invoke: Invoke): Promise { + (await this.channel_.get()).postMessage(JSON.stringify(invoke)); + } - /** - * @hidden - */ - protected inspectReady(): Error | null { - // NO ERROR - if (this.state_ === WorkerServer.State.OPEN) return null; - // ERROR, ONE OF THEM - else if (this.state_ === WorkerServer.State.NONE) - return new DomainError("Server is not opened yet."); - else if (this.state_ === WorkerServer.State.OPENING) - return new DomainError("Server is on opening; wait for a sec."); - else if (this.state_ === WorkerServer.State.CLOSING) - return new RuntimeError("Server is on closing."); - // MAY NOT BE OCCURED - else if (this.state_ === WorkerServer.State.CLOSED) - return new RuntimeError("The server has been closed."); - else return new RuntimeError("Unknown error, but not connected."); - } + /** + * @hidden + */ + protected inspectReady(): Error | null { + // NO ERROR + if (this.state_ === WorkerServer.State.OPEN) return null; + // ERROR, ONE OF THEM + else if (this.state_ === WorkerServer.State.NONE) + return new DomainError("Server is not opened yet."); + else if (this.state_ === WorkerServer.State.OPENING) + return new DomainError("Server is on opening; wait for a sec."); + else if (this.state_ === WorkerServer.State.CLOSING) + return new RuntimeError("Server is on closing."); + // MAY NOT BE OCCURED + else if (this.state_ === WorkerServer.State.CLOSED) + return new RuntimeError("The server has been closed."); + else return new RuntimeError("Unknown error, but not connected."); + } - /** - * @hidden - */ - private _Handle_message(evt: MessageEvent): void { - if (evt.data === WorkerServer.State.CLOSING) this.close(); - else this.replyData(JSON.parse(evt.data)); - } + /** + * @hidden + */ + private _Handle_message(evt: MessageEvent): void { + if (evt.data === WorkerServer.State.CLOSING) this.close(); + else this.replyData(JSON.parse(evt.data)); + } } /** * */ export namespace WorkerServer { - /** - * Current state of the {@link WorkerServer}. - */ - export import State = IServer.State; + /** + * Current state of the {@link WorkerServer}. + */ + export import State = IServer.State; } //---- @@ -277,8 +272,8 @@ export namespace WorkerServer { * @hidden */ interface IFeature { - close(): void; - postMessage(message: any): void; - onmessage(event: MessageEvent): void; - is_worker_server(): boolean; + close(): void; + postMessage(message: any): void; + onmessage(event: MessageEvent): void; + is_worker_server(): boolean; } diff --git a/src/protocols/workers/index.ts b/src/protocols/workers/index.ts index 8b7f3f7..1193241 100644 --- a/src/protocols/workers/index.ts +++ b/src/protocols/workers/index.ts @@ -1,9 +1,6 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- -import * as workers from "./module"; +export * from "./WorkerServer"; +export * from "./WorkerConnector"; -export default workers; -export * from "./module"; +export * from "./SharedWorkerServer"; +export * from "./SharedWorkerAcceptor"; +export * from "./SharedWorkerConnector"; diff --git a/src/protocols/workers/internal/FileSystem.ts b/src/protocols/workers/internal/FileSystem.ts index 77a8765..82c6951 100644 --- a/src/protocols/workers/internal/FileSystem.ts +++ b/src/protocols/workers/internal/FileSystem.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import type fs from "fs"; import { NodeModule } from "../../../utils/internal/NodeModule"; @@ -10,96 +5,96 @@ import { NodeModule } from "../../../utils/internal/NodeModule"; * @hidden */ export namespace FileSystem { - /* ---------------------------------------------------------------- - ACCESSORS - ---------------------------------------------------------------- */ - export async function exists(path: string): Promise { - const { exists } = await NodeModule.fs.get(); - return new Promise((resolve) => { - exists(path, resolve); - }); - } + /* ---------------------------------------------------------------- + ACCESSORS + ---------------------------------------------------------------- */ + export async function exists(path: string): Promise { + const { exists } = await NodeModule.fs.get(); + return new Promise((resolve) => { + exists(path, resolve); + }); + } - export async function dir(path: string): Promise { - const { readdir } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - readdir(path, (err, ret) => { - if (err) reject(err); - else resolve(ret); - }); - }); - } + export async function dir(path: string): Promise { + const { readdir } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + readdir(path, (err, ret) => { + if (err) reject(err); + else resolve(ret); + }); + }); + } - export async function lstat(path: string): Promise { - const { lstat } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - lstat(path, (err, stat) => { - if (err) reject(err); - else resolve(stat); - }); - }); - } + export async function lstat(path: string): Promise { + const { lstat } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + lstat(path, (err, stat) => { + if (err) reject(err); + else resolve(stat); + }); + }); + } - export function read(path: string): Promise; - export function read(path: string, encoding: string): Promise; + export function read(path: string): Promise; + export function read(path: string, encoding: string): Promise; - export async function read( - path: string, - encoding?: string, - ): Promise { - const { readFile } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - const callback = ( - err: NodeJS.ErrnoException | null, - ret: Buffer | string, - ) => { - if (err) reject(err); - else resolve(ret); - }; - if (encoding === undefined) readFile(path, callback); - else readFile(path, encoding as "utf8", callback); - }); - } + export async function read( + path: string, + encoding?: string, + ): Promise { + const { readFile } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + const callback = ( + err: NodeJS.ErrnoException | null, + ret: Buffer | string, + ) => { + if (err) reject(err); + else resolve(ret); + }; + if (encoding === undefined) readFile(path, callback); + else readFile(path, encoding as "utf8", callback); + }); + } - /* ---------------------------------------------------------------- - ARCHIVERS - ---------------------------------------------------------------- */ - export async function mkdir(path: string): Promise { - if ((await exists(path)) === false) await _Mkdir(path); - } + /* ---------------------------------------------------------------- + ARCHIVERS + ---------------------------------------------------------------- */ + export async function mkdir(path: string): Promise { + if ((await exists(path)) === false) await _Mkdir(path); + } - async function _Mkdir(path: string): Promise { - const { mkdir } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - mkdir(path, (err) => { - if (err) reject(err); - else resolve(); - }); - }); - } + async function _Mkdir(path: string): Promise { + const { mkdir } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + mkdir(path, (err) => { + if (err) reject(err); + else resolve(); + }); + }); + } - export async function write( - path: string, - content: string | Buffer, - ): Promise { - const { writeFile } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - const callback = (err: NodeJS.ErrnoException | null) => { - if (err) reject(err); - else resolve(); - }; - if (content instanceof Buffer) writeFile(path, content, callback); - else writeFile(path, content, "utf8", callback); - }); - } + export async function write( + path: string, + content: string | Buffer, + ): Promise { + const { writeFile } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + const callback = (err: NodeJS.ErrnoException | null) => { + if (err) reject(err); + else resolve(); + }; + if (content instanceof Buffer) writeFile(path, content, callback); + else writeFile(path, content, "utf8", callback); + }); + } - export async function unlink(path: string): Promise { - const { unlink } = await NodeModule.fs.get(); - return new Promise((resolve, reject) => { - unlink(path, (err) => { - if (err) reject(err); - else resolve(); - }); - }); - } + export async function unlink(path: string): Promise { + const { unlink } = await NodeModule.fs.get(); + return new Promise((resolve, reject) => { + unlink(path, (err) => { + if (err) reject(err); + else resolve(); + }); + }); + } } diff --git a/src/protocols/workers/internal/IReject.ts b/src/protocols/workers/internal/IReject.ts index dca1bdf..15eb52d 100644 --- a/src/protocols/workers/internal/IReject.ts +++ b/src/protocols/workers/internal/IReject.ts @@ -1,12 +1,7 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- /** * @hidden */ export interface IReject { - name: "reject"; - message: string; + name: "reject"; + message: string; } diff --git a/src/protocols/workers/internal/IWorkerCompiler.ts b/src/protocols/workers/internal/IWorkerCompiler.ts index 54bb763..5efeeb4 100644 --- a/src/protocols/workers/internal/IWorkerCompiler.ts +++ b/src/protocols/workers/internal/IWorkerCompiler.ts @@ -1,22 +1,17 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- /** * @hidden */ export interface IWorkerCompiler { - compile(content: string): Promise; - remove(path: string): Promise; - execute(jsFile: string, argv: string[] | undefined): Promise; + compile(content: string): Promise; + remove(path: string): Promise; + execute(jsFile: string, argv: string[] | undefined): Promise; } /** * @hidden */ export namespace IWorkerCompiler { - export type Creator = { - new (jsFile: string, execArgv: string[] | undefined): IWorkerCompiler; - }; + export type Creator = { + new (jsFile: string, execArgv: string[] | undefined): IWorkerCompiler; + }; } diff --git a/src/protocols/workers/internal/IWorkerSystem.ts b/src/protocols/workers/internal/IWorkerSystem.ts index 7118182..1670a20 100644 --- a/src/protocols/workers/internal/IWorkerSystem.ts +++ b/src/protocols/workers/internal/IWorkerSystem.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- /** * Common interface for communicators based on `Worker`. * @@ -22,14 +17,14 @@ * @author Jeongho Nam - https://github.com/samchon */ export interface IWorkerSystem { - /** - * Close connection. - * - * Close connection between the remote worker system. - * - * It destroies all RFCs (remote function calls) between this and the remote system - * (through `Driver`) that are not returned (completed) yet. The destruction - * causes all incompleted RFCs to throw exceptions. - */ - close(): Promise; + /** + * Close connection. + * + * Close connection between the remote worker system. + * + * It destroies all RFCs (remote function calls) between this and the remote system + * (through `Driver`) that are not returned (completed) yet. The destruction + * causes all incompleted RFCs to throw exceptions. + */ + close(): Promise; } diff --git a/src/protocols/workers/internal/NodeWorkerCompiler.ts b/src/protocols/workers/internal/NodeWorkerCompiler.ts index de38c73..b69c6d5 100644 --- a/src/protocols/workers/internal/NodeWorkerCompiler.ts +++ b/src/protocols/workers/internal/NodeWorkerCompiler.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { v4 } from "uuid"; import { NodeModule } from "../../../utils/internal/NodeModule"; @@ -15,37 +10,36 @@ import { ThreadWorker } from "./threads/ThreadWorker"; * @hidden */ export const NodeWorkerCompiler = async ( - type: "process" | "thread", + type: "process" | "thread", ): Promise => ({ - execute: async (jsFile, execArg) => { - const factory = - type === "process" ? await ProcessWorker() : await ThreadWorker(); - return (new factory(jsFile, execArg)) as Worker; - }, - compile: async (content) => { - const os = await NodeModule.os.get(); - let path: string = `${os.tmpdir().split("\\").join("/")}/tgrid`; - if ((await FileSystem.exists(path)) === false) - await FileSystem.mkdir(path); + execute: async (jsFile, execArg) => { + const factory = + type === "process" ? await ProcessWorker() : await ThreadWorker(); + return (new factory(jsFile, execArg)) as Worker; + }, + compile: async (content) => { + const os = await NodeModule.os.get(); + let path: string = `${os.tmpdir().split("\\").join("/")}/tgrid`; + if ((await FileSystem.exists(path)) === false) await FileSystem.mkdir(path); - while (true) { - const myPath: string = `${path}/${v4()}.js`; - if ((await FileSystem.exists(myPath)) === false) { - path = myPath; - break; - } - } + while (true) { + const myPath: string = `${path}/${v4()}.js`; + if ((await FileSystem.exists(myPath)) === false) { + path = myPath; + break; + } + } - await FileSystem.write(path, content); - return path; - }, - remove: async (url) => { - try { - await FileSystem.unlink(url); - } catch {} - }, - // public execute(jsFile: string, execArgv: string[] | undefined): Worker - // { - // return new this.factory_(jsFile, execArgv) as any; - // } + await FileSystem.write(path, content); + return path; + }, + remove: async (url) => { + try { + await FileSystem.unlink(url); + } catch {} + }, + // public execute(jsFile: string, execArgv: string[] | undefined): Worker + // { + // return new this.factory_(jsFile, execArgv) as any; + // } }); diff --git a/src/protocols/workers/internal/WebWorkerCompiler.ts b/src/protocols/workers/internal/WebWorkerCompiler.ts index 70978e1..4337b2e 100644 --- a/src/protocols/workers/internal/WebWorkerCompiler.ts +++ b/src/protocols/workers/internal/WebWorkerCompiler.ts @@ -1,25 +1,20 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import { IWorkerCompiler } from "./IWorkerCompiler"; /** * @hidden */ export const WebWorkerCompiler = async (): Promise => ({ - compile: async (content) => { - const blob: Blob = new Blob([content], { - type: "application/javascript", - }); - return window.URL.createObjectURL(blob); - }, - remove: async (url) => { - // THE FILE CAN BE REMOVED BY BROWSER AUTOMATICALLY - try { - window.URL.revokeObjectURL(url); - } catch {} - }, - execute: async (jsFile) => new Worker(jsFile), + compile: async (content) => { + const blob: Blob = new Blob([content], { + type: "application/javascript", + }); + return window.URL.createObjectURL(blob); + }, + remove: async (url) => { + // THE FILE CAN BE REMOVED BY BROWSER AUTOMATICALLY + try { + window.URL.revokeObjectURL(url); + } catch {} + }, + execute: async (jsFile) => new Worker(jsFile), }); diff --git a/src/protocols/workers/internal/processes/ProcessChannel.ts b/src/protocols/workers/internal/processes/ProcessChannel.ts index bbba358..e5e4d78 100644 --- a/src/protocols/workers/internal/processes/ProcessChannel.ts +++ b/src/protocols/workers/internal/processes/ProcessChannel.ts @@ -1,27 +1,22 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- /** * @hidden */ export class ProcessChannel { - public static postMessage(message: any): void { - (global.process as Required).send(message); - } + public static postMessage(message: any): void { + (global.process as Required).send(message); + } - public static close(): void { - global.process.exit(); - } + public static close(): void { + global.process.exit(); + } - public static set onmessage(listener: (event: MessageEvent) => void) { - global.process.on("message", (msg) => { - listener({ data: msg } as MessageEvent); - }); - } + public static set onmessage(listener: (event: MessageEvent) => void) { + global.process.on("message", (msg) => { + listener({ data: msg } as MessageEvent); + }); + } - public static is_worker_server(): boolean { - return !!global.process.send; - } + public static is_worker_server(): boolean { + return !!global.process.send; + } } diff --git a/src/protocols/workers/internal/processes/ProcessWorker.ts b/src/protocols/workers/internal/processes/ProcessWorker.ts index 75728f3..5250485 100644 --- a/src/protocols/workers/internal/processes/ProcessWorker.ts +++ b/src/protocols/workers/internal/processes/ProcessWorker.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- import type cp from "child_process"; import { NodeModule } from "../../../../utils/internal/NodeModule"; import { IWorkerCompiler } from "../IWorkerCompiler"; @@ -11,28 +6,28 @@ import { IWorkerCompiler } from "../IWorkerCompiler"; * @hidden */ export async function ProcessWorker(): Promise { - const { fork } = await NodeModule.cp.get(); + const { fork } = await NodeModule.cp.get(); - class ProcessWorker { - private process_: cp.ChildProcess; + class ProcessWorker { + private process_: cp.ChildProcess; - public constructor(jsFile: string, execArgv: string[] | undefined) { - this.process_ = fork(jsFile, { execArgv }); - } + public constructor(jsFile: string, execArgv: string[] | undefined) { + this.process_ = fork(jsFile, { execArgv }); + } - public terminate(): void { - this.process_.kill(); - } + public terminate(): void { + this.process_.kill(); + } - public set onmessage(listener: (event: MessageEvent) => void) { - this.process_.on("message", (message) => { - listener({ data: message } as MessageEvent); - }); - } + public set onmessage(listener: (event: MessageEvent) => void) { + this.process_.on("message", (message) => { + listener({ data: message } as MessageEvent); + }); + } - public postMessage(message: any): void { - this.process_.send(message); - } + public postMessage(message: any): void { + this.process_.send(message); } - return (ProcessWorker) as IWorkerCompiler.Creator; + } + return (ProcessWorker) as IWorkerCompiler.Creator; } diff --git a/src/protocols/workers/internal/threads/ThreadPort.ts b/src/protocols/workers/internal/threads/ThreadPort.ts index e19935d..13299ea 100644 --- a/src/protocols/workers/internal/threads/ThreadPort.ts +++ b/src/protocols/workers/internal/threads/ThreadPort.ts @@ -7,17 +7,17 @@ import { NodeModule } from "../../../../utils/internal/NodeModule"; * @hidden */ export async function ThreadPort() { - const { parentPort } = await NodeModule.thread.get(); - return { - postMessage: (message: any) => parentPort!.postMessage(message), - close: () => global.process.exit(0), - onmessage: (listener: (event: MessageEvent) => void) => - parentPort!.on("message", (message) => - listener({ data: message } as MessageEvent), - ), - document: parentPort - ? (null! as Document) // NOT WORKER - : undefined, - is_worker_server: () => !!parentPort, - }; + const { parentPort } = await NodeModule.thread.get(); + return { + postMessage: (message: any) => parentPort!.postMessage(message), + close: () => global.process.exit(0), + onmessage: (listener: (event: MessageEvent) => void) => + parentPort!.on("message", (message) => + listener({ data: message } as MessageEvent), + ), + document: parentPort + ? (null! as Document) // NOT WORKER + : undefined, + is_worker_server: () => !!parentPort, + }; } diff --git a/src/protocols/workers/internal/threads/ThreadWorker.ts b/src/protocols/workers/internal/threads/ThreadWorker.ts index e6f3b5d..e997761 100644 --- a/src/protocols/workers/internal/threads/ThreadWorker.ts +++ b/src/protocols/workers/internal/threads/ThreadWorker.ts @@ -9,27 +9,27 @@ import { IWorkerCompiler } from "../IWorkerCompiler"; * @hidden */ export async function ThreadWorker(): Promise { - const { Worker } = await NodeModule.thread.get(); - class ThreadWorker { - private readonly worker_: thread.Worker; + const { Worker } = await NodeModule.thread.get(); + class ThreadWorker { + private readonly worker_: thread.Worker; - public constructor(jsFile: string, execArgv: string[] | undefined) { - this.worker_ = new Worker(jsFile, { execArgv }); - } + public constructor(jsFile: string, execArgv: string[] | undefined) { + this.worker_ = new Worker(jsFile, { execArgv }); + } - public terminate(): void { - this.worker_.terminate().catch(() => {}); - } + public terminate(): void { + this.worker_.terminate().catch(() => {}); + } - public set onmessage(listener: (event: MessageEvent) => void) { - this.worker_.on("message", (value) => { - listener({ data: value } as MessageEvent); - }); - } + public set onmessage(listener: (event: MessageEvent) => void) { + this.worker_.on("message", (value) => { + listener({ data: value } as MessageEvent); + }); + } - public postMessage(message: any): void { - this.worker_.postMessage(message); - } + public postMessage(message: any): void { + this.worker_.postMessage(message); } - return (ThreadWorker) as IWorkerCompiler.Creator; + } + return (ThreadWorker) as IWorkerCompiler.Creator; } diff --git a/src/protocols/workers/module.ts b/src/protocols/workers/module.ts deleted file mode 100644 index e2bc284..0000000 --- a/src/protocols/workers/module.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.protocols.workers - */ -//---------------------------------------------------------------- -export * from "./WorkerServer"; -export * from "./WorkerConnector"; - -export * from "./SharedWorkerServer"; -export * from "./SharedWorkerAcceptor"; -export * from "./SharedWorkerConnector"; diff --git a/src/test/browser/index.ts b/src/test/browser/index.ts deleted file mode 100644 index 3258174..0000000 --- a/src/test/browser/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -import puppeteer from "puppeteer"; -const HttpServer = require("local-web-server"); - -const PORT = 37792; -const ROOT = "http://127.0.0.1:" + PORT; - -function _Test_page(page: puppeteer.Page): Promise { - return new Promise((resolve, reject) => { - page.on("framenavigated", resolve); - page.on("close", resolve); - page.on("pageerror", reject); - // page.on("console", msg => - // { - // console.log(msg.text()); - // }); - }); -} - -async function _Paginate( - browser: puppeteer.Browser, - url: string, -): Promise { - console.log("\t" + url); - url = ROOT + "/" + url; - - const page = await browser.newPage(); - await page.goto(url); - await _Test_page(page); -} - -async function main(): Promise { - //---- - // PREPARE SERVER & BROWSER - //---- - const server = new HttpServer().listen({ - directory: __dirname + "/../../../bundle", - port: 37792, - }); - const browser = await puppeteer.launch({ devtools: true }); - - //---- - // TEST PAGES - //---- - // WEB - try { - await import(__dirname + "/web-server.js"); - await _Paginate(browser, "web.html"); - - // WORKERS - await _Paginate(browser, "worker.html"); - await _Paginate(browser, "shared-worker.html"); - } catch (exp) { - console.log("An error has occured"); - console.log(exp); - process.exit(-1); - } - - //---- - // TERMINATES - //---- - await browser.close(); - server.close(); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/browser/internal.ts b/src/test/browser/internal.ts deleted file mode 100644 index f126dfa..0000000 --- a/src/test/browser/internal.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function complete(): void { - let url: string = self.location.href; - const symbol: number = url.indexOf("#"); - if (symbol !== -1) url = url.substr(0, symbol); - - self.location.href = url + "#complete"; -} diff --git a/src/test/browser/shared-worker-client.ts b/src/test/browser/shared-worker-client.ts deleted file mode 100644 index 6c94cd8..0000000 --- a/src/test/browser/shared-worker-client.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { SharedWorkerConnector } from "../../protocols/workers/module"; -import { ICalculator } from "../controllers/ICalculator"; -import { complete } from "./internal"; - -window.onload = async () => { - const worker: SharedWorkerConnector = new SharedWorkerConnector( - null, - null, - ); - - // TEST RE-USABILITY - for (let i: number = 0; i < 5; ++i) { - await worker.connect("shared-worker-server.js"); - - await ICalculator.main(worker.getDriver()); - await worker.close(); - } - complete(); -}; diff --git a/src/test/browser/shared-worker-server.ts b/src/test/browser/shared-worker-server.ts deleted file mode 100644 index 6cd3134..0000000 --- a/src/test/browser/shared-worker-server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { SharedWorkerServer } from "../../protocols/workers/module"; -import { Calculator } from "../providers/Calculator"; - -/// chrome://inspect/#workers -async function main(): Promise { - const server: SharedWorkerServer = - new SharedWorkerServer(); - await server.open(async (acceptor) => { - console.log(acceptor.header); - await acceptor.accept(new Calculator()); - }); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/browser/web-client.ts b/src/test/browser/web-client.ts deleted file mode 100644 index af2cf01..0000000 --- a/src/test/browser/web-client.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { WebConnector } from "../../protocols/web/module"; -import { Driver } from "../../components/module"; -import { InvalidArgument } from "tstl/exception/InvalidArgument"; - -import { ICalculator } from "../controllers/ICalculator"; -import { complete } from "./internal"; - -window.onload = async () => { - for (let i: number = 0; i < 5; ++i) { - const connector: WebConnector = new WebConnector( - null, - null, - ); - await connector.connect("ws://127.0.0.1:10489"); - - const driver: Driver = connector.getDriver(); - if (driver instanceof Driver === false) - throw new InvalidArgument("Error on Driver type checking"); - - await ICalculator.main(driver); - await connector.close(); - } - complete(); -}; diff --git a/src/test/browser/web-server.ts b/src/test/browser/web-server.ts deleted file mode 100644 index 71ba019..0000000 --- a/src/test/browser/web-server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { WebServer } from "../../protocols/web/module"; -import { Calculator } from "../providers/Calculator"; - -async function main(): Promise { - const server: WebServer = new WebServer(); - let index: number = 0; - - await server.open(10489, async (acceptor) => { - await acceptor.accept(new Calculator()); - - await acceptor.join(); - if (++index === 5) await server.close(); - }); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/browser/worker-client.ts b/src/test/browser/worker-client.ts deleted file mode 100644 index ff28ebd..0000000 --- a/src/test/browser/worker-client.ts +++ /dev/null @@ -1,25 +0,0 @@ -import "whatwg-fetch"; - -import { WorkerConnector } from "../../protocols/workers/module"; -import { ICalculator } from "../controllers/ICalculator"; -import { complete } from "./internal"; - -async function get_source(): Promise { - let url: string = location.href; - url = url.substr(0, url.lastIndexOf("/")) + "/worker-server.js"; - - const response: Response = await fetch(url, { method: "GET" }); - return await response.text(); -} - -window.onload = async () => { - const worker: WorkerConnector = new WorkerConnector(null, null); - - for (let i: number = 0; i < 5; ++i) { - await worker.compile(await get_source()); - - await ICalculator.main(worker.getDriver()); - await worker.close(); - } - complete(); -}; diff --git a/src/test/browser/worker-server.ts b/src/test/browser/worker-server.ts deleted file mode 100644 index 3c7480a..0000000 --- a/src/test/browser/worker-server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { WorkerServer } from "../../protocols/workers/WorkerServer"; -import { Calculator } from "../providers/Calculator"; - -async function main(): Promise { - const server: WorkerServer = new WorkerServer(); - await server.open(new Calculator()); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/controllers/ICalculator.ts b/src/test/controllers/ICalculator.ts deleted file mode 100644 index 200df99..0000000 --- a/src/test/controllers/ICalculator.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { DomainError } from "tstl/exception/DomainError"; -import { InvalidArgument } from "tstl/exception/InvalidArgument"; -import { randint } from "tstl/algorithm/random"; - -import { Driver } from "../../components/Driver"; -import { Calculator } from "../providers/Calculator"; - -export interface ICalculator extends ISimple { - scientific: IScientific; - statistics: IStatistics; -} - -export interface ISimple { - 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 IScientific { - pow(x: number, y: number): number; - sqrt(x: number): number; - log(x: number, y: number): number; -} -export interface IStatistics { - mean(...elems: number[]): number; - stdev(...elems: number[]): number; -} - -export namespace ICalculator { - export async function main(driver: Driver): Promise { - // VALIDATOR - const validator: Calculator = new Calculator(); - - // CALL FUNCTIONS IN SERVER FROM CLIENT - for (let i: number = 0; i < 100; ++i) await validate(driver, validator); - - // EXCEPTION THROWN BY THE SERVER - if ((await get_exception(driver)) === null) - throw new DomainError("Throwing exception doesn't work."); - } - - async function validate( - driver: Driver, - validator: Calculator, - ): Promise { - if (driver === validator) - throw new InvalidArgument("Mistaken arguments."); - - // SPECIFY METHODS - const method: IMethod = METHODS[randint(0, METHODS.length - 1)]; - const x: number = randint(2, 10); - const y: number = randint(2, 10); - - // CALL FUNCTION & GET ANSWER - const ret: number = await method(driver, x, y); - const answer: number = method(validator, x, y) as number; - - // VALIDATE - if (ret !== answer) throw new DomainError("Error on function calling."); - } - - export async function get_exception( - driver: Driver, - ): Promise { - try { - await driver.divides(2, 0); - } catch (exp) { - return (exp as Error).message; - } - return null; - } - - const METHODS: IMethod[] = [ - (calc, x, y) => calc.plus(x, y), - (calc, x, y) => calc.minus(x, y), - (calc, x, y) => calc.multiplies(x, y), - (calc, x, y) => calc.divides(x, y), - (calc, x, y) => calc.scientific.pow(x, y), - (calc, x, y) => calc.scientific.log(x, y), - (calc, x) => calc.scientific.sqrt(x), - (calc, x, y) => calc.statistics.mean(x, y), - (calc, x, y) => calc.statistics.stdev(x, y), - ]; - - interface IMethod { - (calculator: Calculator | Driver, x: number, y: number): - | number - | Promise; - } -} diff --git a/src/test/controllers/IChatPrinter.ts b/src/test/controllers/IChatPrinter.ts deleted file mode 100644 index cc88470..0000000 --- a/src/test/controllers/IChatPrinter.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IChatPrinter { - print(name: string, content: string): void; -} diff --git a/src/test/controllers/IChatService.ts b/src/test/controllers/IChatService.ts deleted file mode 100644 index ca1dcd4..0000000 --- a/src/test/controllers/IChatService.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IChatService { - setName(val: string): boolean; - talk(str: string): void; -} diff --git a/src/test/controllers/IScript.ts b/src/test/controllers/IScript.ts deleted file mode 100644 index 0ce51f3..0000000 --- a/src/test/controllers/IScript.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { DomainError } from "tstl/exception/DomainError"; -import { LengthError } from "tstl/exception/LengthError"; - -export interface IScript { - name: string; - message: string; -} - -export namespace IScript { - export const SCENARIO: IScript[] = [ - { - name: "Administrator", - message: "The Chat Application has been started.", - }, - { - name: "Jeongho Nam", - message: "Hello, my name is Jeongho Nam, author of the TGrid.", - }, - { name: "Sam", message: "I'm Sam, nice to meet you." }, - { - name: "Administrator", - message: "Welcome two participants. Nice to meet you too.", - }, - { name: "Jeongho Nam", message: "Nice to meet you three." }, - { name: "Mad Scientist", message: "HAHAHAHAHAHA" }, - { name: "Sam", message: "???????" }, - { - name: "Jeongho Nam", - message: "I'm going to sleep. See you tomorrow.", - }, - { name: "Administrator", message: "See ya~" }, - { name: "Mad Scientist", message: "Conquer all over the world~!!" }, - { name: "Sam", message: "Good bye~!!" }, - ]; - export const PEOPLE: string[] = [ - ...new Set(SCENARIO.map((script) => script.name)), - ]; - - export function validate(scripts: IScript[]): void { - if (scripts.length !== SCENARIO.length) - throw new LengthError("Different length between two scripts."); - - for (let i: number = 0; i < scripts.length; ++i) - if ( - scripts[i].name !== SCENARIO[i].name || - scripts[i].message !== SCENARIO[i].message - ) - throw new DomainError("Different script exists."); - } -} diff --git a/src/test/controllers/IVector.ts b/src/test/controllers/IVector.ts deleted file mode 100644 index 56fbd6c..0000000 --- a/src/test/controllers/IVector.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Driver } from "../../components/module"; - -import { DomainError } from "tstl/exception/DomainError"; -import { randint } from "tstl/algorithm/random"; - -export interface IVector { - size(): T; - at(index: number): T; - set(index: number, val: T): void; - push_back(val: T): void; -} - -export namespace IVector { - export async function main(driver: Driver>): Promise { - let mySum: number = 0; - let serverSum: number = 0; - - for (let i: number = 0; i < 10; ++i) { - const val: number = randint(1, 10); - - mySum += val; - await driver.push_back(val); - } - - for (let i: number = 0; i < (await driver.size()); ++i) - serverSum += await driver.at(i); - - if (mySum !== serverSum) - throw new DomainError("Error on function returning."); - } -} diff --git a/src/test/node/components/test_pseudo.ts b/src/test/node/components/test_pseudo.ts deleted file mode 100644 index fc3e4e6..0000000 --- a/src/test/node/components/test_pseudo.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable */ - -import { Communicator } from "../../../components/Communicator"; -import { Driver } from "../../../components/Driver"; - -import { Calculator } from "../../providers/Calculator"; -import { ICalculator } from "../../controllers/ICalculator"; -import { Invoke } from "../../../components/Invoke"; -import { InvalidArgument } from "tstl/exception/InvalidArgument"; - -class PseudoCommunicator extends Communicator { - private sender_: (invoke: Invoke) => void; - - public constructor(sender: (invoke: Invoke) => void, provider: Provider) { - super(provider); - this.sender_ = sender; - } - - protected inspectReady(): Error | null { - return null; - } - protected async sendData(invoke: Invoke): Promise { - this.sender_(invoke); - } - public reply(invoke: Invoke): void { - this.replyData(invoke); - } -} - -export async function test_pseudo(): Promise { - //---- - // SERVER & CLIENT - //---- - // PRE-DECLARATIONS - let server: PseudoCommunicator; - let client: PseudoCommunicator; - - // CONSTRUCT SYSTEMS - server = new PseudoCommunicator( - (ivk) => client.reply(ivk), - new Calculator(), - ); - client = new PseudoCommunicator((ivk) => server.reply(ivk), null); - - //---- - // INTERACTS - //---- - // GET DRIVER - const driver: Driver = client.getDriver(); - if (driver instanceof Driver !== true) - throw new InvalidArgument("Error on Driver type checking"); - - // DO TEST - await ICalculator.main(driver); -} diff --git a/src/test/node/components/test_security.ts b/src/test/node/components/test_security.ts deleted file mode 100644 index edec991..0000000 --- a/src/test/node/components/test_security.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { WebServer } from "../../../protocols/web/WebServer"; -import { WebConnector } from "../../../protocols/web/WebConnector"; - -import { Driver } from "../../../components/Driver"; -import { Calculator, Scientific } from "../../providers/Calculator"; - -class CustomCalculator extends Calculator { - public _scientific = new Scientific(); - - public multiplies_(x: number, y: number): number { - return x * y; - } -} - -async function must_be_error( - procedures: (() => Promise)[], -): Promise { - let count: number = 0; - for (const proc of procedures) - try { - await proc(); - } catch (exp) { - ++count; - } - if (count !== procedures.length) throw new Error("Must be security error"); -} - -export async function test_security(): Promise { - const server: WebServer = new WebServer(); - await server.open(10101, async (acceptor) => { - await acceptor.accept(new CustomCalculator()); - }); - - const connector: WebConnector = new WebConnector(null, null); - await connector.connect("ws://127.0.0.1:10101"); - - const calc: Driver = connector.getDriver(); - await must_be_error([ - () => (calc.plus as any).toString(), - () => (calc as any).prototype.toString(), - // () => calc._scientific.log(2, 16), - // () => calc.multiplies_(4, 5), - () => (calc.scientific as any).constructor.toString(), - () => (calc.statistics.mean as any).prototype.minus(7, 4), - ]); - - await connector.close(); - await server.close(); -} diff --git a/src/test/node/index.ts b/src/test/node/index.ts deleted file mode 100644 index 1264906..0000000 --- a/src/test/node/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -const EXTENSION = __filename.substr(-2); -if (EXTENSION === "js") require("source-map-support").install(); - -import fs from "fs"; - -interface IModule { - [key: string]: () => Promise; -} - -async function iterate(path: string): Promise { - const file_list: string[] = fs.readdirSync(path); - for (const file of file_list) { - const current_path: string = path + "/" + file; - const stat: fs.Stats = fs.lstatSync(current_path); - - if (stat.isDirectory() === true && file !== "internal") { - await iterate(current_path); - continue; - } else if ( - file.substr(-3) !== ".js" || - current_path === __dirname + "/index.js" - ) - continue; - - const external: IModule = await import( - current_path.substr(0, current_path.length - 3) - ); - for (const key in external) - if (key.substr(0, 5) === "test_") { - // WHEN SPECIALIZED - if ( - process.argv[2] && - key.replace("test_", "") !== process.argv[2] - ) - continue; - - // DO TEST - console.log(key); - await external[key](); - } - } -} - -async function main(): Promise { - //---- - // DO TEST - //---- - const time: number = Date.now(); - await iterate(__dirname); - - //---- - // TRACE BENCHMARK - //---- - // ELAPSED TIME - console.log("----------------------------------------------------------"); - console.log("Success"); - console.log(` - elapsed time: ${(Date.now() - time).toLocaleString()} ms`); - - // MEMORY USAGE - const memory: NodeJS.MemoryUsage = process.memoryUsage(); - for (const property in memory) { - const amount: number = - memory[property as keyof NodeJS.MemoryUsage] / 10 ** 6; - console.log(` - ${property}: ${amount.toLocaleString()} MB`); - } -} -main().catch((e) => { - console.log(e); - process.exit(1); -}); diff --git a/src/test/node/protocols/web/test_web_calculator.ts b/src/test/node/protocols/web/test_web_calculator.ts deleted file mode 100644 index 652f4ac..0000000 --- a/src/test/node/protocols/web/test_web_calculator.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { WebServer } from "../../../../protocols/web/WebServer"; -import { WebConnector } from "../../../../protocols/web/WebConnector"; -import { Driver } from "../../../../components/Driver"; - -import { Calculator } from "../../../providers/Calculator"; -import { ICalculator } from "../../../controllers/ICalculator"; -import { IVector } from "../../../controllers/IVector"; - -import { Vector } from "tstl/container/Vector"; - -const PORT: number = 10101; - -export async function test_web_calculator(): Promise { - //---- - // SERVER - //---- - const server: WebServer> = - new WebServer(); - await server.open(PORT, async (acceptor) => { - // SPEICFY PROVIDER - const provider = /calculator/.test(acceptor.path) - ? new Calculator() - : new Vector(); - - // ALLOW CONNECTION - await acceptor.accept(provider); - }); - - //---- - // CLIENTS - //---- - const connector: WebConnector = new WebConnector(null, null); - - // const RE-USABILITY - for (const path of ["calculator", "vector"]) - for (let i: number = 0; i < 3; ++i) { - // DO CONNECT - await connector.connect(`ws://127.0.0.1:${PORT}/${path}`); - - // SET DRIVER AND TEST BY CALCULATOR PROCESS - if (path === "calculator") { - const driver: Driver = connector.getDriver(); - await ICalculator.main(driver); - } else { - const driver: Driver> = connector.getDriver(); - await IVector.main(driver); - } - await connector.close(); - } - - // CLOSE SERVER - await server.close(); -} diff --git a/src/test/node/protocols/web/test_web_chat.ts b/src/test/node/protocols/web/test_web_chat.ts deleted file mode 100644 index 74da771..0000000 --- a/src/test/node/protocols/web/test_web_chat.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { WebServer, WebConnector } from "../../../../protocols/web/module"; -import { Driver } from "../../../../components/Driver"; - -import { IScript } from "../../../controllers/IScript"; -import { IChatPrinter } from "../../../controllers/IChatPrinter"; -import { IChatService } from "../../../controllers/IChatService"; -import { ChatService } from "../../../providers/ChatService"; - -import { sleep_for } from "tstl/thread/global"; - -const PORT = 10101; - -/* ---------------------------------------------------------------- - CLIENT ----------------------------------------------------------------- */ -class Client { - private name_!: string; - private connector_!: WebConnector; - private scripts_!: IScript[]; - - private service_!: Driver; - - public async participate(name: string): Promise { - // ASSIGN MEMBERS - this.name_ = name; - this.connector_ = new WebConnector(null, { - print: (name: string, message: string): void => { - this.scripts_.push({ name: name, message: message }); - }, - }); - this.service_ = this.connector_.getDriver(); - this.scripts_ = []; - - // PREPARE INTERACTION - await this.connector_.connect("http://127.0.0.1:" + PORT); - await this.service_.setName(name); - } - - public async shout(): Promise { - for (const script of IScript.SCENARIO) { - await sleep_for(50); - if (script.name === this.name_) - await this.service_.talk(script.message); - } - } - - public async close(): Promise { - await this.connector_.close(); - return this.scripts_; - } -} - -/* ---------------------------------------------------------------- - SERVER ----------------------------------------------------------------- */ -class Server { - private server_!: WebServer; - private scripts_!: IScript[]; - - public async open(): Promise { - this.server_ = new WebServer(); - this.scripts_ = []; - - await this.server_.open(PORT, async (acceptor) => { - const service: ChatService = new ChatService(); - service.assign(acceptor.getDriver(), this.scripts_); - - await acceptor.accept(service); - await acceptor.join(); - - service.destroy(); - }); - } - - public async close(): Promise { - await this.server_.close(); - return this.scripts_; - } -} - -/* ---------------------------------------------------------------- - MAIN ----------------------------------------------------------------- */ -export async function test_web_chat(): Promise { - // OPEN SERVER - const server: Server = new Server(); - await server.open(); - - // PREPARE CLIENTS - const clients: Client[] = []; - for (const name of IScript.PEOPLE) { - const c: Client = new Client(); - await c.participate(name); - - clients.push(c); - } - - // START CHATTING - const promiseList: Promise[] = []; - for (const c of clients) promiseList.push(c.shout()); - await Promise.all(promiseList); - - // VALIDATIONS - for (const c of clients) { - const scripts: IScript[] = await c.close(); - IScript.validate(scripts); - } - IScript.validate(await server.close()); -} diff --git a/src/test/node/protocols/web/test_web_header.ts b/src/test/node/protocols/web/test_web_header.ts deleted file mode 100644 index 6f6056d..0000000 --- a/src/test/node/protocols/web/test_web_header.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { WebServer } from "../../../../protocols/web/WebServer"; -import { WebConnector } from "../../../../protocols/web/WebConnector"; - -const PORT = 10101; -const TOKEN = "asdfawe4fasdfchswrtgadfg"; - -interface IHeaders { - token: string; -} - -export async function test_web_header(): Promise { - const server: WebServer = new WebServer(); - await server.open(PORT, async (acceptor) => { - if (acceptor.header.token !== TOKEN) await acceptor.reject(); - else await acceptor.accept(null); - }); - - const connector: WebConnector = new WebConnector( - { token: TOKEN }, - null, - ); - await connector.connect(`ws://127.0.0.1:${PORT}`); - await connector.close(); - - await server.close(); -} diff --git a/src/test/node/protocols/web/test_web_mutex.ts b/src/test/node/protocols/web/test_web_mutex.ts deleted file mode 100644 index 95d23b7..0000000 --- a/src/test/node/protocols/web/test_web_mutex.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { WebServer } from "../../../../protocols/web/WebServer"; -import { WebConnector } from "../../../../protocols/web/WebConnector"; -import { Driver } from "../../../../components/Driver"; - -import { Vector } from "tstl/container/Vector"; -import { Mutex } from "tstl/thread/Mutex"; -import { DomainError } from "tstl/exception/DomainError"; -import { RuntimeError } from "tstl/exception/RuntimeError"; - -import { is_sorted } from "tstl/algorithm/sorting"; -import { sleep_for } from "tstl/thread/global"; - -const PORT: number = 10101; -const COUNT: number = 10; -const SLEEP_TIME: number = 10; - -class Provider { - public mutex: Mutex; - public vector: Vector; - private index_: number; - - public constructor(mutex: Mutex, vector: Vector, index: number) { - this.mutex = mutex; - this.vector = vector; - this.index_ = index; - } - - public getIndex(): number { - return this.index_; - } -} - -async function _Test_client(): Promise { - const connector: WebConnector = new WebConnector(null, null); - await connector.connect(`ws://127.0.0.1:${PORT}`); - - const driver: Driver = connector.getDriver(); - await driver.mutex.lock(); - { - const index: number = await driver.getIndex(); - - await sleep_for((COUNT - index) * SLEEP_TIME); - await driver.vector.push_back(index); - } - await driver.mutex.unlock(); - - await connector.close(); -} - -export async function test_web_mutex(): Promise { - //---- - // PREPARES - //---- - // OPEN SERVER - const server: WebServer = new WebServer(); - const mutex: Mutex = new Mutex(); - const vector: Vector = new Vector(); - let index: number = 0; - - await server.open(PORT, async (acceptor) => { - await acceptor.accept(new Provider(mutex, vector, index++)); - }); - - // PREPARE LATCH & TIME RECORDER - const promiseList: Promise[] = []; - const time: number = Date.now(); - - // CREATE CLIENTS - for (let i: number = 0; i < COUNT; ++i) promiseList.push(_Test_client()); - - // WAIT PROMISES - await Promise.all(promiseList); - - //---- - // VALIDATIONS - //---- - // MUTEX.LOCK CONSUMED PROPER TIME? - if (Date.now() - time < COUNT * ((COUNT * SLEEP_TIME) / 2.0)) - throw new RuntimeError("remote mutex lock is not exact."); - - // ELEMENTS MUST BE SORTED BY THE CRITICAL SECTION - if (is_sorted(vector.begin(), vector.end()) === false) - throw new DomainError( - "remote mutex lock does not ensure the critical section.", - ); - - // CLOSE THE SERVER - await server.close(); -} diff --git a/src/test/node/protocols/web/test_web_reject.ts b/src/test/node/protocols/web/test_web_reject.ts deleted file mode 100644 index e38da4a..0000000 --- a/src/test/node/protocols/web/test_web_reject.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { WebServer } from "../../../../protocols/web/WebServer"; -import { WebConnector } from "../../../../protocols/web/WebConnector"; - -const PORT = 10101; - -export async function test_web_reject(): Promise { - const server = new WebServer(); - - // TEST RE-USABILITY TOO - for (let i: number = 0; i < 5; ++i) { - await server.open(PORT, async (acceptor) => { - await acceptor.reject(1001, "Rejected by test automation program."); - }); - - const connector: WebConnector = new WebConnector( - null, - null, - ); - let error: Error | null = null; - - try { - await connector.connect(`ws://127.0.0.1:${PORT}`); - } catch (exp) { - error = exp as Error; - } - await server.close(); - - if (error === null) throw new Error("Catching reject has failed."); - } -} diff --git a/src/test/node/protocols/web/test_web_server_close.ts b/src/test/node/protocols/web/test_web_server_close.ts deleted file mode 100644 index c66dcc4..0000000 --- a/src/test/node/protocols/web/test_web_server_close.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { WebServer } from "../../../../protocols/web/WebServer"; -import { WebConnector } from "../../../../protocols/web/WebConnector"; - -const PORT = 10101; - -export async function test_web_server_close(): Promise { - const server: WebServer = new WebServer(); - await server.open(PORT, (acceptor) => acceptor.accept(null)); - - for (let i: number = 0; i < 100; ++i) { - const connector: WebConnector = new WebConnector( - null, - null, - ); - await connector.connect(`ws://127.0.0.1:${PORT}`); - } - await server.close(); -} diff --git a/src/test/node/protocols/workers/internal/calculator.ts b/src/test/node/protocols/workers/internal/calculator.ts deleted file mode 100644 index a47eca5..0000000 --- a/src/test/node/protocols/workers/internal/calculator.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { WorkerServer } from "../../../../../protocols/workers/WorkerServer"; -import { WorkerConnector } from "../../../../../protocols/workers/WorkerConnector"; -import { Driver } from "../../../../../components/Driver"; - -import { Simple } from "../../../../providers/Calculator"; -import { IScientific, IStatistics } from "../../../../controllers/ICalculator"; - -class HierarchicalCalculator extends Simple { - // REMOTE CALCULATOR - public scientific!: Driver; - public statistics!: Driver; -} - -async function get( - path: string, -): Promise> { - // DO CONNECT - const connector: WorkerConnector = new WorkerConnector( - null, - null, - ); - await connector.connect(path); - - // RETURN DRIVER - return connector.getDriver(); -} - -async function main(): Promise { - // PREPARE REMOTE CALCULATOR - const calc = new HierarchicalCalculator(); - calc.scientific = await get(__dirname + "/scientific.js"); - calc.statistics = await get(__dirname + "/statistics.js"); - - // OPEN SERVER - const server = new WorkerServer(); - await server.open(calc); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/node/protocols/workers/internal/chat-child.ts b/src/test/node/protocols/workers/internal/chat-child.ts deleted file mode 100644 index 1ab67fa..0000000 --- a/src/test/node/protocols/workers/internal/chat-child.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { WorkerServer } from "../../../../../protocols/workers/WorkerServer"; -import { IScript } from "../../../../controllers/IScript"; -import { Driver } from "../../../../../components/Driver"; -import { IChatService } from "../../../../controllers/IChatService"; - -import { sleep_for } from "tstl/thread/global"; - -async function main(): Promise { - //---- - // PREPARATIONS - //---- - // OPEN SERVER - const server: WorkerServer<{ name: string }, object> = new WorkerServer(); - const scripts: IScript[] = []; - - // PREPARE ASSETS - const myName: string = (await server.getHeader()).name; - const service: Driver = server.getDriver(); - - await server.open({ - shout: async () => { - for (const script of IScript.SCENARIO) { - await sleep_for(50); - if (script.name === myName) await service.talk(script.message); - } - }, - print: (name: string, message: string) => { - scripts.push({ name: name, message: message }); - }, - validate: () => { - IScript.validate(scripts); - }, - }); - await service.setName(myName); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/node/protocols/workers/internal/join.ts b/src/test/node/protocols/workers/internal/join.ts deleted file mode 100644 index 81c8308..0000000 --- a/src/test/node/protocols/workers/internal/join.ts +++ /dev/null @@ -1,16 +0,0 @@ -import fs from "fs"; -import { WorkerServer } from "../../../../../protocols/workers/WorkerServer"; - -const FILE_PATH = __dirname + "/../log.dat"; - -async function main(): Promise { - const server: WorkerServer = new WorkerServer(); - await server.open(null); - await server.join(); - - await fs.promises.writeFile(FILE_PATH, "WorkerServer.join()", "utf8"); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/node/protocols/workers/internal/scientific.ts b/src/test/node/protocols/workers/internal/scientific.ts deleted file mode 100644 index 6140248..0000000 --- a/src/test/node/protocols/workers/internal/scientific.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { WorkerServer } from "../../../../../protocols/workers/module"; -import { Scientific } from "../../../../providers/Calculator"; - -async function main(): Promise { - const server: WorkerServer = new WorkerServer(); - await server.open(new Scientific()); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/node/protocols/workers/internal/statistics.ts b/src/test/node/protocols/workers/internal/statistics.ts deleted file mode 100644 index 08451cc..0000000 --- a/src/test/node/protocols/workers/internal/statistics.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { WorkerServer } from "../../../../../protocols/workers/module"; -import { Statistics } from "../../../../providers/Calculator"; - -async function main(): Promise { - const server: WorkerServer = new WorkerServer(); - await server.open(new Statistics()); -} -main().catch((exp) => { - console.log(exp); - process.exit(-1); -}); diff --git a/src/test/node/protocols/workers/test_hierarchical_workers.ts b/src/test/node/protocols/workers/test_hierarchical_workers.ts deleted file mode 100644 index 06993e6..0000000 --- a/src/test/node/protocols/workers/test_hierarchical_workers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { WorkerConnector } from "../../../../protocols/workers/module"; -import { Driver } from "../../../../components/module"; - -import { ICalculator } from "../../../controllers/ICalculator"; - -export async function test_hierarchical_workers(): Promise { - const connector: WorkerConnector = new WorkerConnector( - null, - null, - ); - for (let i: number = 0; i < 5; ++i) { - // DO CONNECT - await connector.connect(__dirname + "/internal/calculator.js"); - - // DO TEST - const driver: Driver = connector.getDriver(); - await ICalculator.main(driver); - - // TERMINATE - await connector.close(); - } -} diff --git a/src/test/node/protocols/workers/test_worker.ts b/src/test/node/protocols/workers/test_worker.ts deleted file mode 100644 index b7f31a4..0000000 --- a/src/test/node/protocols/workers/test_worker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { WorkerConnector } from "../../../../protocols/workers/module"; -import { IScientific } from "../../../controllers/ICalculator"; - -export async function test_worker(): Promise { - const worker: WorkerConnector = new WorkerConnector(null, null); - await worker.connect(__dirname + "/internal/scientific.js"); - - if ((await worker.getDriver().pow(2, 4)) !== Math.pow(2, 4)) - throw new Error("Unknown error on worker"); - await worker.close(); -} diff --git a/src/test/node/protocols/workers/test_worker_chat.ts b/src/test/node/protocols/workers/test_worker_chat.ts deleted file mode 100644 index 4a0bd59..0000000 --- a/src/test/node/protocols/workers/test_worker_chat.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { DomainError } from "tstl/exception/DomainError"; -import { LengthError } from "tstl/exception/LengthError"; - -export interface IScript { - name: string; - message: string; -} - -export namespace IScript { - export const SCENARIO: IScript[] = [ - { - name: "Administrator", - message: "The Chat Application has been started.", - }, - { - name: "Jeongho Nam", - message: "Hello, my name is Jeongho Nam, author of the TGrid.", - }, - { name: "Sam", message: "I'm Sam, nice to meet you." }, - { - name: "Administrator", - message: "Welcome two participants. Nice to meet you too.", - }, - { name: "Jeongho Nam", message: "Nice to meet you three." }, - { name: "Mad Scientist", message: "HAHAHAHAHAHA" }, - { name: "Sam", message: "???????" }, - { - name: "Jeongho Nam", - message: "I'm going to sleep. See you tomorrow.", - }, - { name: "Administrator", message: "See ya~" }, - { name: "Mad Scientist", message: "Conquer all over the world~!!" }, - { name: "Sam", message: "Good bye~!!" }, - ]; - export const PEOPLE: string[] = [ - ...new Set(SCENARIO.map((script) => script.name)), - ]; - - export function validate(scriptList: IScript[]): void { - // VALIDATE LENGTH - if (scriptList.length !== SCENARIO.length) - throw new LengthError("Different length between two scripts."); - - // VALIDATE MESSAGES - for (const script of scriptList) { - const index: number = SCENARIO.findIndex((s) => { - return s.name === script.name && s.message === script.message; - }); - if (index === -1) throw new DomainError("Different script exists."); - } - } -} diff --git a/src/test/node/protocols/workers/test_worker_compiler.ts b/src/test/node/protocols/workers/test_worker_compiler.ts deleted file mode 100644 index 87b8a4c..0000000 --- a/src/test/node/protocols/workers/test_worker_compiler.ts +++ /dev/null @@ -1,41 +0,0 @@ -import fs from "fs"; -import cp from "child_process"; - -import { WorkerConnector } from "../../../../protocols/workers/WorkerConnector"; -import { ICalculator } from "../../../controllers/ICalculator"; - -export function test_worker_connect(): Promise { - return _Test_worker( - (worker) => - worker.connect(__dirname + "/../../../browser/worker-server.js"), - "process", - ); -} - -export async function test_worker_compile(): Promise { - const PATH = __dirname + "/../../../../../bundle/worker-server.js"; - if (fs.existsSync(PATH) === false) cp.execSync("npm run bundle"); - - await _Test_worker( - (worker) => worker.compile(fs.readFileSync(PATH, "utf8")), - "thread", - ); -} - -async function _Test_worker( - connect: (obj: WorkerConnector) => Promise, - type: "thread" | "process", -): Promise { - const worker: WorkerConnector = new WorkerConnector( - null, - null, - type, - ); - - // TEST RE-USABILITY - for (let i: number = 0; i < 5; ++i) { - await connect(worker); - await ICalculator.main(worker.getDriver()); - await worker.close(); - } -} diff --git a/src/test/node/protocols/workers/test_worker_join.ts b/src/test/node/protocols/workers/test_worker_join.ts deleted file mode 100644 index 13f4ae3..0000000 --- a/src/test/node/protocols/workers/test_worker_join.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { WorkerConnector } from "../../../../protocols/workers/module"; - -import { FileSystem } from "../../../../protocols/workers/internal/FileSystem"; -import { sleep_for } from "tstl/thread"; - -const FILE_PATH = __dirname + "/log.dat"; - -export async function test_worker_join(): Promise { - await FileSystem.write(FILE_PATH, "NOT YET"); - - const connector: WorkerConnector = new WorkerConnector( - null, - null, - ); - await connector.connect(__dirname + "/internal/join.js"); - - sleep_for(100) - .then(() => connector.close()) - .catch(() => {}); - await connector.join(); - - const content: string = await FileSystem.read(FILE_PATH, "utf8"); - await FileSystem.unlink(FILE_PATH); - - if (content !== "WorkerServer.join()") - throw new Error("Error on WorkerServer.join()"); -} diff --git a/src/test/node/test_exports.ts b/src/test/node/test_exports.ts deleted file mode 100644 index 5d2c1ed..0000000 --- a/src/test/node/test_exports.ts +++ /dev/null @@ -1,85 +0,0 @@ -import * as fs from "fs"; - -import * as index from "../../index"; -import { HashMap } from "tstl/container/HashMap"; -import { sort } from "tstl/ranges/algorithm/sorting"; -import { ArrayUtil } from "./utils/ArrayUtil"; - -const MAIN = __dirname + "/../.."; -const HELPERS = [MAIN + "/test"]; -const EXCLUDES = ["internal"]; - -async function from_dynamic( - map: HashMap, - path: string, -): Promise { - const fileList: string[] = await fs.promises.readdir(path); - for (const file of fileList) { - const current: string = `${path}/${file}`; - const stats: fs.Stats = await fs.promises.stat(current); - - if ( - stats.isDirectory() && - ArrayUtil.has(HELPERS, current) === false && - ArrayUtil.has(EXCLUDES, file) === false - ) - await from_dynamic(map, current); - else if ( - file.substr(-3) === __filename.substr(-3) && - __filename.substr(-5) !== ".d.ts" - ) { - const elem: any = await import(current); - for (const key in elem) { - const instance: any = elem[key]; - if (!instance) continue; - - const type: string = typeof instance; - if ( - type === "function" || - (type === "object" && - !( - instance.constructor === Object && - key[0].toLowerCase() === key[0] - )) - ) - map.set(instance, key); - } - } - } -} - -function from_index(map: HashMap, elem: any): void { - if (map.has(elem) === true) return; - - for (const key in elem) { - const instance: any = elem[key]; - const type: string = typeof instance; - - if (type === "object" && instance.constructor === Object) - if (key[0].toUpperCase() === key[0]) map.set(instance, key); - else from_index(map, instance); - else if (typeof instance === "function" || typeof instance === "object") - map.set(instance, key); - } -} - -export async function test_exports(): Promise { - const dynamicMap: HashMap = new HashMap(); - const indexMap: HashMap = new HashMap(); - - await from_dynamic(dynamicMap, MAIN); - from_index(indexMap, index); - - const ommissions: string[] = []; - for (const tuple of dynamicMap) - if (indexMap.has(tuple.first) === false) ommissions.push(tuple.second); - - if (ommissions.length !== 0) { - sort(ommissions); - throw new Error( - "Error on module indexing: omitted " + - ommissions.join(", ") + - " components.", - ); - } -} diff --git a/src/test/node/utils/ArrayUtil.ts b/src/test/node/utils/ArrayUtil.ts deleted file mode 100644 index 6a9571b..0000000 --- a/src/test/node/utils/ArrayUtil.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { equal_to } from "tstl/functional/comparators"; -import { randint } from "tstl/algorithm/random"; - -export namespace ArrayUtil { - export function has( - elements: T[], - target: T, - predicator: (x: T, y: T) => boolean = equal_to, - ): boolean { - return elements.find((elem) => predicator(elem, target)) !== undefined; - } - - export function random(elements: readonly T[]): T { - const index: number = randint(0, elements.length - 1); - return elements[index]; - } -} diff --git a/src/test/node/utils/test_url_variables.ts b/src/test/node/utils/test_url_variables.ts deleted file mode 100644 index 736c435..0000000 --- a/src/test/node/utils/test_url_variables.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { DomainError } from "tstl/exception/DomainError"; -import { LengthError } from "tstl/exception/LengthError"; -import { InvalidArgument } from "tstl/exception/InvalidArgument"; -import { Pair } from "tstl/utility/Pair"; -import { equal } from "tstl/algorithm/iterations"; - -import { URLVariables } from "../../../utils/URLVariables"; - -type Element = Pair; -interface IAuthor { - name: string; - age: number; - git: string; - homepage: string; - memo: string; - is_crazy: boolean; - - [key: string]: any; -} - -function test_global(author: IAuthor): void { - //---- - // STRINGIFY & PARSE - //---- - // STRINGIFY -> OBJECT TO URL-ENCODED STRING - const url_encoded_str: string = URLVariables.stringify(author); - - // PARSE -> URL-ENCODED STRING TO OBJECT - const obj: IAuthor = URLVariables.parse(url_encoded_str, true); - - //---- - // VALIDATE - //---- - // VALIDATE STRINGIFY - if (url_encoded_str !== URLVariables.stringify(obj)) - throw new DomainError("Error on URLVariables.decode()."); - - // VALIDATE PARSE -> (AUTHOR === OBJ)? - for (const key in author) - if (author[key] !== obj[key]) - throw new DomainError("Error on URLVariables.parse()."); -} - -function test_class(author: IAuthor): void { - //---- - // GENERATE URL-VARIABLES OBJECT - //---- - const dict: URLVariables = new URLVariables(); - - // FILL ELEMENTS - dict.set("name", author.name); - dict.set("age", String(author.age)); // MUST BE STRING - dict.set("git", author.git); - dict.set("homepage", author.homepage); - dict.set("memo", author.memo); - dict.set("is_crazy", String(author.is_crazy)); - - // CONVERT THE URL-VARIABLES OBJECT TO URL-ENCODED STRING - const url_encoded_str: string = dict.toString(); - - //---- - // VALIDATIONS - //---- - // CREATE A NEW URL-VARIABLES OBJECT - // BY PARSING THE URL-ENCODED STRING - const vars: URLVariables = new URLVariables(url_encoded_str); - - // VALIDATE SIZE - if (dict.size() !== vars.size()) - throw new LengthError("Size are different."); - - // ALL ELEMENTS ARE EQUAL - const equality: boolean = equal( - dict.begin(), - dict.end(), - vars.begin(), - function (x: Element, y: Element): boolean { - return x.first === y.first && x.second === y.second; - }, - ); - if (equality === false) - throw new InvalidArgument("Elements are different."); - - // ALL ELEMENTS ARE EQUAL, THEN ENCODINGS MUST BE SAME - if (dict.toString() !== vars.toString()) - throw new DomainError("Error on URLVariables.toString()."); -} - -export function test_url_variables(): void { - const author: IAuthor = { - name: "Samchon (Jeongho Nam)", - age: 29, - git: "https://github.com/samchon/tstl", - homepage: "http://samchon.org", - memo: "Hello, I'm the best programmer in Korea.", - is_crazy: true, - }; - test_global(author); - test_class(author); -} diff --git a/src/test/node/utils/test_xml.ts b/src/test/node/utils/test_xml.ts deleted file mode 100644 index 43989d0..0000000 --- a/src/test/node/utils/test_xml.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { DomainError } from "tstl/exception/DomainError"; -import { Pair } from "tstl/utility/Pair"; -import { Vector } from "tstl/container/Vector"; - -import { XML } from "../../../utils/XML"; -import { XMLList } from "../../../utils/XMLList"; - -class Invoke extends Vector { - public listener: string; - - public constructor(listener: string) { - super(); - - this.listener = listener; - } - - public toXML(): XML { - const ret: XML = new XML(); - ret.setTag("invoke"); - ret.setProperty("listener", this.listener); - - for (const param of this) ret.push(param.toXML()); - - return ret; - } -} - -class Parameter { - public name: string; - public type: string; - public value: string | XML | null; - - public constructor( - name: string, - type: string, - value: string | XML | null = null, - ) { - this.name = name; - this.type = type; - this.value = value; - } - - public toXML(): XML { - const ret: XML = new XML(); - ret.setTag("parameter"); - - ret.setProperty("name", this.name); - ret.setProperty("type", this.type); - - if (this.value !== null) - if (this.value instanceof XML) ret.push(this.value as XML); - else ret.setValue(this.value as string); - - return ret; - } -} - -class Member { - public id: string; - public email: string; - public name: string; - - public constructor(id: string, email: string, name: string) { - this.id = id; - this.email = email; - this.name = name; - } - - public toXML(): XML { - const ret: XML = new XML(); - ret.setTag("member"); - - ret.setProperty("id", this.id); - ret.setProperty("email", this.email); - ret.setProperty("name", this.name); - - return ret; - } -} - -function validate_equality(x: T, y: T): void { - if (x !== y) throw new DomainError("Error on XML Parser."); -} - -function write(): Pair> { - const invoke: Invoke = new Invoke("setMemberList"); - invoke.push_back(new Parameter("application", "string", "simulation")); - invoke.push_back(new Parameter("sequence", "number", "3")); - - const members: Vector = new Vector(); - members.push( - new Member("samchon", "samchon@samchon.org", "Jeongho Nam"), - new Member("github", "github@github.com", "GitHub"), - new Member("robot", "google@google.com", "AlphaGo"), - ); - - const memberList: XML = new XML(); - memberList.setTag("memberList"); - - for (const elem of members) memberList.push(elem.toXML()); - - invoke.push_back(new Parameter("memberList", "XML", memberList)); - - return new Pair(invoke, members); -} - -function read(pair: Pair>): void { - const invoke: Invoke = pair.first; - const members: Vector = pair.second; - - // CREATE AN XML OBJECT BY PARSING CHARACTERS - const xml: XML = pair.first.toXML(); - - //---- - // CONVERTING TESTS - //---- - const xml2: XML = new XML(xml.toString()); - const xml3: XML = new XML(xml); - - validate_equality(xml.toString(), xml2.toString()); - validate_equality(xml2.toString(), xml3.toString()); - - //---- - // LIST OF PARAMETER OBJECTS - //---- - // XML => HashMap - // XMLList => Vector - let xmlList: XMLList = xml.get("parameter"); - - // VALIDATE SIZE - validate_equality(xmlList.size(), invoke.size()); - - // VALIDATE MEMBERS OF PARAMETERS - for (let i: number = 0; i < invoke.size(); ++i) { - const param: Parameter = invoke.at(i); - const child: XML = xmlList.at(i); - - validate_equality(param.name, child.getProperty("name")); - validate_equality(param.type, child.getProperty("type")); - - if (typeof param.value === "string") - validate_equality(param.value, child.getValue()); - } - - //---- - // ACCESS TO CHILDREN XML OBJECTS - //---- - xmlList = xml.get("parameter").at(2).get("memberList").at(0).get("member"); - - // VALIDATE SIZE - validate_equality(xmlList.size(), members.size()); - - // VALIDATE MEMBERS OF MEMBERS - for (let i: number = 0; i < members.size(); ++i) { - const member: Member = members.at(i); - const child: XML = xmlList.at(i); - - validate_equality(member.id, child.getProperty("id")); - validate_equality(member.email, child.getProperty("email")); - validate_equality(member.name, child.getProperty("name")); - } -} - -export function test_xml(): void { - read(write()); -} diff --git a/src/test/providers/Calculator.ts b/src/test/providers/Calculator.ts deleted file mode 100644 index afd1fae..0000000 --- a/src/test/providers/Calculator.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - ICalculator, - ISimple, - IScientific, - IStatistics, -} from "../controllers/ICalculator"; - -export class Simple implements ISimple { - public plus(x: number, y: number): number { - return x + y; - } - public minus(x: number, y: number): number { - return x - y; - } - - public multiplies(x: number, y: number): number { - return x * y; - } - public divides(x: number, y: number): number { - if (y === 0) throw new Error("Divided by zero."); - return x / y; - } -} - -export class Calculator extends Simple implements ICalculator { - public scientific = new Scientific(); - public statistics = new Statistics(); -} - -export class Scientific implements IScientific { - public pow(x: number, y: number): number { - return Math.pow(x, y); - } - - public log(x: number, y: number): number { - if (x < 0 || y < 0) throw new Error("Negative value on log."); - return Math.log(y) / Math.log(x); - } - - public sqrt(x: number): number { - if (x < 0) throw new Error("Negative value on sqaure."); - return Math.sqrt(x); - } -} - -export class Statistics implements IStatistics { - public mean(...elems: number[]): number { - let ret: number = 0; - for (const val of elems) ret += val; - return ret / elems.length; - } - - public stdev(...elems: number[]): number { - const mean: number = this.mean(...elems); - let ret: number = 0; - - for (const val of elems) ret += Math.pow(val - mean, 2); - - return Math.sqrt(ret / elems.length); - } -} diff --git a/src/test/providers/ChatService.ts b/src/test/providers/ChatService.ts deleted file mode 100644 index 8231fb2..0000000 --- a/src/test/providers/ChatService.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { IChatService } from "../controllers/IChatService"; - -import { Driver } from "../../components/module"; -import { HashMap } from "tstl/container/HashMap"; -import { IChatPrinter } from "../controllers/IChatPrinter"; - -import { IScript } from "../controllers/IScript"; -import { DomainError } from "tstl/exception/DomainError"; - -export class ChatService implements IChatService { - private static participants_: HashMap> = - new HashMap(); - private scripts_!: IScript[]; - private driver_!: Driver; - - private name_?: string; - - public assign(driver: Driver, scripts: IScript[]): void { - this.driver_ = driver; - this.scripts_ = scripts; - } - - public destroy(): void { - if (this.name_) ChatService.participants_.erase(this.name_); - } - - public setName(str: string): boolean { - if (ChatService.participants_.has(str)) return false; // DUPLICATED NAME - - // SET NAME AND ENROLL IT TO DICTIONARY - this.name_ = str; - ChatService.participants_.emplace(str, this.driver_); - - // INFORMS TRUE TO CLIENT - return true; - } - - public talk(content: string): void { - if (!this.name_) throw new DomainError("No name"); - - // INFORM TO EVERYBODY - for (const it of ChatService.participants_) { - const driver: Driver = it.second; - - // INFORM IT TO CLIENT - const promise: Promise = driver.print(this.name_, content); - - // DISCONNECTION WHILE TALKING MAY POSSIBLE - promise.catch(() => {}); - } - - // LOG ARCHIVE - this.scripts_.push({ name: this.name_, message: content }); - } -} diff --git a/src/components/Driver.ts b/src/typings/Driver.ts similarity index 78% rename from src/components/Driver.ts rename to src/typings/Driver.ts index 495955d..713192d 100644 --- a/src/components/Driver.ts +++ b/src/typings/Driver.ts @@ -1,9 +1,4 @@ -/** - * @packageDocumentation - * @module tgrid.components - */ -//---------------------------------------------------------------- -import { Promisive } from "../typings/Promisive"; +import { Promisive } from "./Promisive"; /** * Driver RFC (Remote Function Call). @@ -23,7 +18,7 @@ import { Promisive } from "../typings/Promisive"; * @author Jeongho Nam - https://github.com/samchon */ export type Driver< - Controller extends object, - Parametric extends boolean = false, + Controller extends object, + Parametric extends boolean = false, > = typeof Driver & Readonly>; export const Driver = class {}; diff --git a/src/typings/Functional.ts b/src/typings/Functional.ts index b86f76c..409c559 100644 --- a/src/typings/Functional.ts +++ b/src/typings/Functional.ts @@ -1,9 +1,3 @@ -/* eslint-disable */ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- import { Parametric } from "./Parametric"; import { Primitive } from "./Primitive"; @@ -20,33 +14,33 @@ import { Primitive } from "./Primitive"; * @author Jeongho Nam - https://github.com/samchon */ export type Functional< - Method extends Function, - UseParametric extends boolean = false, + Method extends Function, + UseParametric extends boolean = false, > = (Method extends (...args: infer Params) => infer Ret - ? Ret extends Promise - ? ( - ...args: FunctionalParams - ) => Promise> - : ( - ...args: FunctionalParams - ) => Promise> - : (...args: any[]) => Promise) & - IRemoteFunction; + ? Ret extends Promise + ? ( + ...args: FunctionalParams + ) => Promise> + : ( + ...args: FunctionalParams + ) => Promise> + : (...args: any[]) => Promise) & + IRemoteFunction; /** * @hidden */ type FunctionalParams< - Params extends any[], - UseParametric extends boolean, + Params extends any[], + UseParametric extends boolean, > = UseParametric extends true ? Parametric : Params; /** * Restrictions for Remote Function */ type IRemoteFunction = { - /** - * Remote Function does not allow it. - */ - [P in keyof Function | "Symbol"]: never; + /** + * Remote Function does not allow it. + */ + [P in keyof Function | "Symbol"]: never; }; diff --git a/src/typings/IJsonable.ts b/src/typings/IJsonable.ts index d48fa8e..6e8947e 100644 --- a/src/typings/IJsonable.ts +++ b/src/typings/IJsonable.ts @@ -1,14 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- -/** - * JSON convertible. - * - * @template T Target type to be converted - * @author Jeongho Nam - https://github.com/samchon - */ export interface IJsonable { - toJSON(): T; + toJSON(): T; } diff --git a/src/typings/OmitEdgeUnderscored.ts b/src/typings/OmitEdgeUnderscored.ts index 5372cb5..ff2ae79 100644 --- a/src/typings/OmitEdgeUnderscored.ts +++ b/src/typings/OmitEdgeUnderscored.ts @@ -1,18 +1,7 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- -/** - * Omit edge underscored member. - * - * @template T Target instance type. - * @author Jeongho Nam - https://github.com/samchon - */ export type OmitEdgeUnderscored = { - [P in keyof T]: P extends `_${string}` - ? never - : P extends `${string}_` - ? never - : T[P]; + [P in keyof T]: P extends `_${string}` + ? never + : P extends `${string}_` + ? never + : T[P]; }; diff --git a/src/typings/Parametric.ts b/src/typings/Parametric.ts index 4ab6615..7dbac64 100644 --- a/src/typings/Parametric.ts +++ b/src/typings/Parametric.ts @@ -1,26 +1,15 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- import { IJsonable } from "./IJsonable"; import { Primitive } from "./Primitive"; import { ValueOf } from "./ValueOf"; -/** - * Convert parameters to be compatible with primitive. - * - * @template Arguments List of parameters - * @author Jeongho Nam - https://github.com/samchon - */ export type Parametric = { - [P in keyof Arguments]: ParametricValue; + [P in keyof Arguments]: ParametricValue; }; /** * @hidden */ type ParametricValue = - | ValueOf - | Primitive - | IJsonable>; + | ValueOf + | Primitive + | IJsonable>; diff --git a/src/typings/Primitive.ts b/src/typings/Primitive.ts index 8ff88d8..e500dca 100644 --- a/src/typings/Primitive.ts +++ b/src/typings/Primitive.ts @@ -1,62 +1,136 @@ -/* eslint-disable */ /** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- -/** - * Primitify a type. + * Primitive type of JSON. + * + * `Primitive` is a TMP (Type Meta Programming) type which converts + * its argument as a primitive type within framework JSON. + * + * If the target argument is a built-in class which returns its origin primitive type + * through the `valueOf()` method like the `String` or `Number`, its return type would + * be the `string` or `number`. Otherwise, the built-in class does not have the + * `valueOf()` method, the return type would be an empty object (`{}`). * - * If target type is an object, all methods defined in the object would be - * removed. Also, if the target type has a `toJSON()` method, its return type - * would be chosen. + * Otherwise, the target argument is a type of custom class, all of its custom method + * would be erased and its prototype would be changed to the primitive `object`. + * Therefore, return type of the TMP type finally be the primitive object. * - * @template Instance An instance type to be primitive + * In addition, if the target argument is a type of custom class and it has a special + * method `toJSON()`, return type of this `Primitive` would be not `Primitive` + * but `Primitive>`. + * + * Before | After + * ------------------------|---------------------------------------- + * `Boolean` | `boolean` + * `Number` | `number` + * `String` | `string` + * `Class` | `object` + * `Class` with `toJSON()` | `Primitive>` + * Native Class | never + * Others | No change + * + * @template T Target argument type. * @author Jeongho Nam - https://github.com/samchon + * @author Kyungsu Kang - https://github.com/kakasoo + * @author Michael - https://github.com/8471919 */ -export type Primitive = value_of extends object - ? Instance extends object - ? Instance extends IJsonable - ? value_of extends object - ? Raw extends object +export type Primitive = + Equal> extends true ? T : PrimitiveMain; + +type Equal = X extends Y ? (Y extends X ? true : false) : false; + +type PrimitiveMain = Instance extends [never] + ? never // (special trick for jsonable | null) type + : ValueOf extends bigint + ? never + : ValueOf extends boolean | number | string + ? ValueOf + : Instance extends Function + ? never + : ValueOf extends object + ? Instance extends object + ? Instance extends NativeClass + ? never + : Instance extends IJsonable + ? ValueOf extends object + ? Raw extends object ? PrimitiveObject // object would be primitified : never // cannot be - : value_of // atomic value - : PrimitiveObject // object would be primitified - : never // cannot be - : value_of; + : ValueOf // atomic value + : PrimitiveObject // object would be primitified + : never // cannot be + : ValueOf; -type PrimitiveObject = Instance extends Array - ? Primitive[] +type PrimitiveObject = + Instance extends Array + ? IsTuple extends true + ? PrimitiveTuple + : PrimitiveMain[] : { - [P in keyof Instance]: Instance[P] extends Function - ? never - : Primitive; + [P in keyof Instance]: PrimitiveMain; }; -type value_of = is_value_of extends true +type PrimitiveTuple = T extends [] + ? [] + : T extends [infer F] + ? [PrimitiveMain] + : T extends [infer F, ...infer Rest extends readonly any[]] + ? [PrimitiveMain, ...PrimitiveTuple] + : T extends [(infer F)?] + ? [PrimitiveMain?] + : T extends [(infer F)?, ...infer Rest extends readonly any[]] + ? [PrimitiveMain?, ...PrimitiveTuple] + : []; + +type ValueOf = + IsValueOf extends true ? boolean - : is_value_of extends true - ? number - : is_value_of extends true - ? string - : Instance; - -type is_value_of< - Instance, - Object extends IValueOf, -> = Instance extends Object - ? Object extends IValueOf - ? Instance extends Primitive - ? false - : true // not Primitive, but Object - : false // cannot be + : IsValueOf extends true + ? number + : IsValueOf extends true + ? string + : Instance; + +type NativeClass = + | Set + | Map + | WeakSet + | WeakMap + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array + | BigUint64Array + | Int8Array + | Int16Array + | Int32Array + | BigInt64Array + | Float32Array + | Float64Array + | ArrayBuffer + | SharedArrayBuffer + | DataView; + +type IsTuple = [T] extends [ + never, +] + ? false + : T extends readonly any[] + ? number extends T["length"] + ? false + : true : false; +type IsValueOf> = Instance extends Object + ? Object extends IValueOf + ? Instance extends U + ? false + : true // not Primitive, but Object + : false // cannot be + : false; + interface IValueOf { - valueOf(): T; + valueOf(): T; } interface IJsonable { - toJSON(): T; + toJSON(): T; } diff --git a/src/typings/Promisive.ts b/src/typings/Promisive.ts index 422ecc7..c757b74 100644 --- a/src/typings/Promisive.ts +++ b/src/typings/Promisive.ts @@ -1,9 +1,3 @@ -/* eslint-disable */ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- import { Functional } from "./Functional"; import { OmitEdgeUnderscored } from "./OmitEdgeUnderscored"; import { RemoveNever } from "./RemoveNever"; @@ -22,33 +16,33 @@ import { ValueOf } from "./ValueOf"; * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. */ export type Promisive< - Instance extends object, - UseParametric extends boolean = false, + Instance extends object, + UseParametric extends boolean = false, > = RemoveNever< - OmitEdgeUnderscored< - { - [P in keyof Instance]: Instance[P] extends Function - ? Functional // function, its return type would be capsuled in the Promise - : ValueOf extends object - ? Instance[P] extends object - ? Promisive // object would be promisified - : never // cannot be - : never; // atomic value - } & IRemoteObject - > + OmitEdgeUnderscored< + { + [P in keyof Instance]: Instance[P] extends Function + ? Functional // function, its return type would be capsuled in the Promise + : ValueOf extends object + ? Instance[P] extends object + ? Promisive // object would be promisified + : never // cannot be + : never; // atomic value + } & IRemoteObject + > >; /** * Restrictions for Remote Object */ interface IRemoteObject { - /** - * Remote Object does not allow access to the `constructor`. - */ - constructor: never; + /** + * Remote Object does not allow access to the `constructor`. + */ + constructor: never; - /** - * Remote Object does not allow access to the `prototype`. - */ - prototype: never; + /** + * Remote Object does not allow access to the `prototype`. + */ + prototype: never; } diff --git a/src/typings/RemoveNever.ts b/src/typings/RemoveNever.ts index 6875825..295654c 100644 --- a/src/typings/RemoveNever.ts +++ b/src/typings/RemoveNever.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- import { SpecialFields } from "./SpecialFields"; /** diff --git a/src/typings/SpecialFields.ts b/src/typings/SpecialFields.ts index d530ba7..9c39d81 100644 --- a/src/typings/SpecialFields.ts +++ b/src/typings/SpecialFields.ts @@ -1,8 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- /** * Pick special fields meeting special type. * @@ -12,5 +7,5 @@ * @author Jeongho Nam - https://github.com/samchon */ export type SpecialFields = { - [P in keyof Instance]: Instance[P] extends Target ? P : never; + [P in keyof Instance]: Instance[P] extends Target ? P : never; }[keyof Instance & string]; diff --git a/src/typings/ValueOf.ts b/src/typings/ValueOf.ts index 56e6907..f0534e7 100644 --- a/src/typings/ValueOf.ts +++ b/src/typings/ValueOf.ts @@ -1,34 +1,29 @@ -/* eslint-disable */ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- /** * Get origin value type. * * @template Instance Target instance * @author Jeongho Nam - https://github.com/samchon */ -export type ValueOf = is_value_of extends true +export type ValueOf = + is_value_of extends true ? boolean : is_value_of extends true - ? number - : is_value_of extends true - ? string - : Instance; + ? number + : is_value_of extends true + ? string + : Instance; type is_value_of< - Instance, - Object extends IValueOf, + Instance, + Object extends IValueOf, > = Instance extends Object - ? Object extends IValueOf - ? Instance extends Primitive - ? false - : true // not Primitive, but Object - : false // cannot be - : false; + ? Object extends IValueOf + ? Instance extends Primitive + ? false + : true // not Primitive, but Object + : false // cannot be + : false; interface IValueOf { - valueOf(): T; + valueOf(): T; } diff --git a/src/typings/index.ts b/src/typings/index.ts index 9b9ed74..214f1d1 100644 --- a/src/typings/index.ts +++ b/src/typings/index.ts @@ -1,9 +1,3 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- -import * as typings from "./module"; - -export default typings; -export * from "./module"; +export * from "./Driver"; +export * from "./Primitive"; +export * from "./Promisive"; diff --git a/src/typings/module.ts b/src/typings/module.ts deleted file mode 100644 index 7428f32..0000000 --- a/src/typings/module.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.typings - */ -//---------------------------------------------------------------- -export * from "./Functional"; -export * from "./IJsonable"; -export * from "./OmitEdgeUnderscored"; -export * from "./Parametric"; -export * from "./Primitive"; -export * from "./Promisive"; -export * from "./RemoveNever"; -export * from "./SpecialFields"; -export * from "./ValueOf"; diff --git a/src/utils/URLVariables.ts b/src/utils/URLVariables.ts deleted file mode 100644 index 355aba6..0000000 --- a/src/utils/URLVariables.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -import { HashMap } from "tstl/container/HashMap"; - -import { Dictionary } from "./internal/Dictionary"; -import { Primitive } from "../typings/Primitive"; - -/** - * URLVariables class is for representing variables of HTTP. - * - * The `URLVariables` class allows you to transfer variables between an application and server. - * - * When transfering, the `URLVariables` should be converted to a *URI* string through {@link toString}(). - * - URI: Uniform Resource Identifier - * - * @reference http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLVariables.html - * @author Migrated by Jeongho Nam - https://github.com/samchon - */ -export class URLVariables - extends Dictionary - implements Omit, "toJSON"> -{ - /** - * Default Constructor. - */ - public constructor(); - - /** - * Construct from a URL-encoded string. - * - * The {@link decode decode()} method is automatically called to convert the string to properties of the {@link URLVariables} object. - * - * @param str A URL-encoded string containing name/value pairs. - */ - public constructor(str: string); - - public constructor(str: string = "") { - super(); - - if (str !== "") this._Parse(str); - } - - /** - * @hidden - */ - private _Parse(str: string): void { - this.clear(); - if (str.trim() === "") return; - - if (str.indexOf("?") !== -1) str = str.substr(str.indexOf("?") + 1); - if (str.indexOf("#") !== -1) str = str.substr(0, str.indexOf("#")); - - const elements: string[] = str.split("&"); - for (const pair of elements) { - const equal_index: number = pair.indexOf("="); - this.emplace( - equal_index === -1 ? pair : pair.substring(0, equal_index), - equal_index === -1 - ? "" - : decodeURIComponent(pair.substring(equal_index + 1)), - ); - } - } - - /** - * Returns a string containing all enumerable variables, in the MIME content encoding application/x-www-form-urlencoded. - */ - public toString(): string { - const elements: string[] = []; - for (const it of this) - if (it.second !== "") - elements.push(`${it.first}=${encodeURIComponent(it.second)}`); - else elements.push(it.first); - return elements.join("&"); - } - - public toJSON(): string { - return this.toString(); - } -} - -export namespace URLVariables { - export type Iterator = HashMap.Iterator; - export type ReverseIterator = HashMap.ReverseIterator; - - export type Serialize = { - [P in keyof Instance]: Primitive extends object - ? never - : Primitive; - }; - - export function parse( - str: string, - autoCase: boolean = true, - ): Serialize { - const variables: URLVariables = new URLVariables(str); - const ret: any = {}; - - for (const entry of variables) { - if (!autoCase || entry.second === "") { - ret[entry.first] = entry.second; - continue; - } - - if (entry.second.length === 0) ret[entry.first] = true; - else if (entry.second === "true" || entry.second === "false") - ret[entry.first] = entry.second === "true"; - else if (Number.isNaN(Number(entry.second)) === false) - ret[entry.first] = Number(entry.second); - else ret[entry.first] = entry.second; - } - return ret as Serialize; - } - - export function stringify(obj: T): string { - if (typeof obj === "boolean" || typeof obj === "number") - return String(obj); - else if (typeof obj === "string") return obj; - else if (obj instanceof URLVariables) return obj.toString(); - - const variables: URLVariables = new URLVariables(); - for (const key in obj) variables.set(key, String(obj[key])); - - return variables.toString(); - } -} diff --git a/src/utils/XML.ts b/src/utils/XML.ts deleted file mode 100644 index 9b62723..0000000 --- a/src/utils/XML.ts +++ /dev/null @@ -1,521 +0,0 @@ -/* eslint-disable */ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -import { Dictionary } from "./internal/Dictionary"; -import { XMLList } from "./XMLList"; - -import { IPair } from "tstl/utility/IPair"; -import { Pair } from "tstl/utility/Pair"; -import { HashMap } from "tstl/container/HashMap"; - -import { DomainError } from "tstl/exception/DomainError"; -import { OutOfRange } from "tstl/exception/OutOfRange"; - -/** - * The XML parser - * - * @author Jeongho Nam - https://github.com/samchon - */ -export class XML - extends Dictionary - implements Omit, "toJSON"> -{ - /** - * @hidden - */ - private tag_!: string; - - /** - * @hidden - */ - private value_!: string; - - /** - * @hidden - */ - private property_map_!: HashMap; - - /* ============================================================= - CONSTRUCTORS - - BASIC CONSTRUCTORS - - PARSERS - ================================================================ - BASIC CONSTRUCTORS - ------------------------------------------------------------- */ - public constructor(); - public constructor(str: string); - public constructor(xml: XML); - - public constructor(obj?: string | XML) { - super(); - - if (obj instanceof XML) this._Copy_constructor(obj); - else { - this.property_map_ = new HashMap(); - this.value_ = ""; - - if (obj !== undefined && typeof obj === "string") - this._Parser_constructor(obj); - } - } - - /** - * @hidden - */ - private _Copy_constructor(obj: XML): void { - // COPY MEMBERS - this.tag_ = obj.tag_; - this.value_ = obj.value_; - this.property_map_ = new HashMap(obj.property_map_); - - // COPY CHILDREN - for (const entry of obj) { - const xml_list: XMLList = new XMLList(); - for (const child of entry.second) - xml_list.push_back(new XML(child)); - - this.emplace(entry.first, xml_list); - } - } - - /** - * @hidden - */ - private _Parser_constructor(str: string): void { - if (str.indexOf("<") === -1) return; - - let start: number; - let end!: number; - - //ERASE HEADER OF XML - if ((start = str.indexOf("", start); - - if (end !== -1) str = str.substr(end + 2); - } - - //ERASE COMMENTS - while ((start = str.indexOf("", start); - if (end === -1) break; - - str = str.substr(0, start) + str.substr(end + 3); - } - - // ERASE !DOCTYPE - start = str.indexOf("") { - ++close_cnt; - end = i; - - if (open_cnt === close_cnt) break; - } - } - - if (open_cnt !== close_cnt) - throw new DomainError( - "Error on XML.constructor(): invalid XML format was found on the ", - ); - - str = str.substr(0, start) + str.substr(end + 1); - } - - //BEGIN PARSING - this._Parse(str); - } - - /* ------------------------------------------------------------- - PARSERS - ------------------------------------------------------------- */ - /** - * @hidden - */ - private _Parse(str: string): void { - this._Parse_tag(str); - this._Parse_properties(str); - - const res = this._Parse_value(str); - if (res.second === true) this._Parse_children(res.first); - } - - /** - * @hidden - */ - private _Parse_tag(str: string): void { - const start: number = str.indexOf("<") + 1; - const end: number = XML._Compute_min_index( - str.indexOf(" ", start), - str.indexOf("\r\n", start), - str.indexOf("\n", start), - str.indexOf("\t", start), - str.indexOf(">", start), - str.indexOf("/", start), - ); - if (start === 0 || end === -1) - throw new DomainError( - "Error on XML.constructor(): invalid XML format, unable to parse tag.", - ); - - this.tag_ = str.substring(start, end); - } - - /** - * @hidden - */ - private _Parse_properties(str: string): void { - let start: number = str.indexOf("<" + this.tag_) + this.tag_.length + 1; - const end: number = XML._Compute_min_index( - str.lastIndexOf("/"), - str.indexOf(">", start), - ); - - if (start === -1 || end === -1 || start >= end) return; - - // : " label='ABCD' " - const line: string = str.substring(start, end); - if (line.indexOf("=") === -1) return; - - let label: string; - let value: string; - let helpers: IQuote[] = []; - - let inQuote: boolean = false; - let quoteType!: number; - let equal: number; - - //INDEXING - for (let i: number = 0; i < line.length; ++i) { - //Start of quote - if ( - inQuote === false && - (line.charAt(i) === "'" || line.charAt(i) === '"') - ) { - inQuote = true; - start = i; - - if (line.charAt(i) === "'") quoteType = 1; - else if (line.charAt(i) === '"') quoteType = 2; - } else if ( - inQuote === true && - ((quoteType === 1 && line.charAt(i) === "'") || - (quoteType === 2 && line.charAt(i) === '"')) - ) { - helpers.push({ type: quoteType, start: start, end: i }); - inQuote = false; - } - } - - //CONSTRUCTING - for (let i: number = 0; i < helpers.length; ++i) { - if (i === 0) { - equal = line.indexOf("="); - label = line.substring(0, equal).trim(); - } else { - equal = line.indexOf("=", helpers[i - 1].end + 1); - label = line.substring(helpers[i - 1].end + 1, equal).trim(); - } - value = line.substring(helpers[i].start + 1, helpers[i].end); - - this.setProperty(label, XML.decode_property(value)); - } - } - - /** - * @hidden - */ - private _Parse_value(str: string): Pair { - const end_slash: number = str.lastIndexOf("/"); - const end_block: number = str.indexOf(">"); - - if (end_slash < end_block || end_slash + 1 === str.lastIndexOf("<")) { - //STATEMENT1: - //STATEMENT2: -> SAME WITH STATEMENT1: - this.value_ = ""; - - return new Pair(str, false); - } - - const start: number = end_block + 1; - const end: number = str.lastIndexOf("<"); - str = str.substring(start, end); //REDEFINE WEAK_STRING -> IN TO THE TAG - - if (str.indexOf("<") === -1) this.value_ = XML.decode_value(str.trim()); - else this.value_ = ""; - - return new Pair(str, true); - } - - /** - * @hidden - */ - private _Parse_children(str: string): void { - if (str.indexOf("<") === -1) return; - - let start: number = str.indexOf("<"); - let end: number = str.lastIndexOf(">") + 1; - str = str.substring(start, end); - - let blockStart: number = 0; - let blockEnd: number = 0; - start = 0; - - for (let i: number = 0; i < str.length; ++i) { - if (str.charAt(i) === "<" && str.substr(i, 2) !== "" || str.substr(i, 2) === "= 1 && blockStart === blockEnd) { - end = str.indexOf(">", i); - - const xml: XML = new XML(); - xml._Parse(str.substring(start, end + 1)); - - let xmlList: XMLList; - if (this.has(xml.tag_) === true) xmlList = this.get(xml.tag_); - else { - xmlList = new XMLList(); - this.set(xml.tag_, xmlList); - } - xmlList.push(xml); - - i = end; - start = end + 1; - blockStart = 0; - blockEnd = 0; - } - } - } - - /* ============================================================= - ACCESSORS - - GETTERS - - SETTERS - - ELEMENTS I/O - ================================================================ - GETTERS - ------------------------------------------------------------- */ - public getTag(): string { - return this.tag_; - } - public getValue(): string { - return this.value_; - } - - public findProperty(key: string): HashMap.Iterator { - return this.property_map_.find(key); - } - public hasProperty(key: string): boolean { - return this.property_map_.has(key); - } - public getProperty(key: string): string { - return this.property_map_.get(key); - } - - public getPropertyMap(): HashMap { - return this.property_map_; - } - - /* ------------------------------------------------------------- - SETTERS - ------------------------------------------------------------- */ - public setTag(val: string): void { - this.tag_ = val; - } - public setValue(val: string): void { - this.value_ = val; - } - public insertValue(tag: string, value: string): XML { - const xml = new XML(); - xml.setTag(tag); - xml.setValue(value); - - this.push(xml); - return xml; - } - - public setProperty(key: string, value: string): void { - this.property_map_.set(key, value); - } - public eraseProperty(key: string): void { - const it = this.property_map_.find(key); - if (it.equals(this.property_map_.end()) === true) - throw new OutOfRange( - "Error on XML.eraseProperty(): unable to find the matched key.", - ); - - this.property_map_.erase(it); - } - - /* ------------------------------------------------------------- - ELEMENTS I/O - ------------------------------------------------------------- */ - public push(...args: IPair[]): number; - public push(...xmls: XML[]): number; - public push(...xmlLists: XMLList[]): number; - - public push(...items: any[]): number { - for (const elem of items) - if (elem instanceof XML) - if (this.has(elem.tag_) === true) - this.get(elem.tag_).push(elem); - else { - const xmlList: XMLList = new XMLList(); - xmlList.push(elem); - - this.set(elem.tag_, xmlList); - } - else if (elem instanceof XMLList) - if (elem.empty() === true) continue; - else if (this.has(elem.getTag()) === true) { - const xmlList: XMLList = this.get(elem.getTag()); - xmlList.insert(xmlList.end(), elem.begin(), elem.end()); - } else this.set(elem.getTag(), elem); - else super.push(elem); - - return this.size(); - } - - /** - * @hidden - */ - protected _Handle_insert( - first: HashMap.Iterator, - last: HashMap.Iterator, - ): void { - for (let it = first; !it.equals(last); it = it.next()) { - const tag: string = it.first; - const xmlList: XMLList = it.second; - - for (const xml of xmlList) - if (xml.getTag() !== tag) xml.setTag(tag); - } - super._Handle_insert(first, last); - } - - /* ------------------------------------------------------------- - STRING UTILS - ------------------------------------------------------------- */ - public toJSON(): string { - return this.toString(); - } - - public toString(): string; - - /** - * @internal - */ - public toString(level: number): string; - - public toString(tab: number = 0): string { - let str: string = "\t".repeat(tab) + "<" + this.tag_; - - //PROPERTIES - for (const entry of this.property_map_) - str += - " " + - entry.first + - '="' + - XML.encode_property(entry.second) + - '"'; - - if (this.size() === 0) { - // VALUE - if (this.value_ !== "") - str += - ">" + - XML.encode_value(this.value_) + - ""; - else str += " />"; - } else { - // CHILDREN - str += ">\n"; - for (const entry of this) str += entry.second.toString(tab + 1); - - str += "\t".repeat(tab) + ""; - } - return str; - } - - /** - * @hidden - */ - private static _Compute_min_index(...args: number[]): number { - let min: number = -1; - - for (const elem of args) - if (elem === -1) continue; - else if (min === -1 || elem < min) min = elem; - - return min; - } -} - -export namespace XML { - export type Iterator = HashMap.Iterator; - export type ReverseIterator = HashMap.ReverseIterator; - - export function head(encoding: string = "utf-8"): string { - return ``; - } - - export function encode_value(str: string): string { - for (const p of VALUE_CODES) str = str.split(p.first).join(p.second); - return str; - } - export function encode_property(str: string): string { - for (const p of PROPERTY_CODES) str = str.split(p.first).join(p.second); - return str; - } - - export function decode_value(str: string): string { - for (const p of VALUE_CODES) str = str.split(p.second).join(p.first); - return str; - } - export function decode_property(str: string): string { - for (const p of PROPERTY_CODES) str = str.split(p.second).join(p.first); - return str; - } - - /** - * @hidden - */ - const VALUE_CODES: Pair[] = [ - new Pair("&", "&"), - new Pair("<", "<"), - new Pair(">", ">"), - ]; - - /** - * @hidden - */ - const PROPERTY_CODES: Pair[] = [ - ...VALUE_CODES, - new Pair('"', """), - new Pair("'", "'"), - new Pair("\t", " "), - new Pair("\n", " "), - new Pair("\r", " "), - ]; -} - -/** - * @hidden - */ -interface IQuote { - type: number; - start: number; - end: number; -} diff --git a/src/utils/XMLList.ts b/src/utils/XMLList.ts deleted file mode 100644 index 9d89cd0..0000000 --- a/src/utils/XMLList.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -import { Vector } from "tstl/container/Vector"; -import { XML } from "./XML"; - -/** - * List of {@link XML}. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export class XMLList extends Vector { - public getTag(): string { - return this.front().getTag(); - } - - public toString(): string; - - /** - * @internal - */ - public toString(level: number): string; - - public toString(level: number = 0): string { - const lines: string[] = []; - for (const xml of this) lines.push(xml.toString(level)); - return lines.join("\n"); - } -} - -export namespace XMLList { - export type Iterator = Vector.Iterator; - export type ReverseIterator = Vector.ReverseIterator; -} diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index ebfa60d..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -import * as utils from "./module"; - -export default utils; -export * from "./module"; diff --git a/src/utils/internal/Dictionary.ts b/src/utils/internal/Dictionary.ts deleted file mode 100644 index a1cda3a..0000000 --- a/src/utils/internal/Dictionary.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -import { HashMap } from "tstl/container/HashMap"; - -/** - * @hidden - */ -export class Dictionary extends HashMap { - /** - * @inheritDoc - */ - public toJSON(): any { - return super.toJSON(); - } -} diff --git a/src/utils/internal/NodeModule.ts b/src/utils/internal/NodeModule.ts index 03c0f4f..f9ad207 100644 --- a/src/utils/internal/NodeModule.ts +++ b/src/utils/internal/NodeModule.ts @@ -9,25 +9,25 @@ import type * as __ws from "ws"; import { Singleton } from "tstl/thread/Singleton"; export namespace NodeModule { - export const cp: Singleton> = new Singleton(() => - import("child_process"), - ); - export const fs: Singleton> = new Singleton(() => - import("fs"), - ); - export const http: Singleton> = new Singleton(() => - import("http"), - ); - export const https: Singleton> = new Singleton(() => - import("https"), - ); - export const os: Singleton> = new Singleton(() => - import("os"), - ); - export const thread: Singleton> = new Singleton( - () => import("worker_threads"), - ); - export const ws: Singleton> = new Singleton(() => - import("ws"), - ); + export const cp: Singleton> = new Singleton( + () => import("child_process"), + ); + export const fs: Singleton> = new Singleton( + () => import("fs"), + ); + export const http: Singleton> = new Singleton( + () => import("http"), + ); + export const https: Singleton> = new Singleton( + () => import("https"), + ); + export const os: Singleton> = new Singleton( + () => import("os"), + ); + export const thread: Singleton> = new Singleton( + () => import("worker_threads"), + ); + export const ws: Singleton> = new Singleton( + () => import("ws"), + ); } diff --git a/src/utils/module.ts b/src/utils/module.ts deleted file mode 100644 index dd31971..0000000 --- a/src/utils/module.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @packageDocumentation - * @module tgrid.utils - */ -//---------------------------------------------------------------- -export * from "./URLVariables"; -export * from "./XML"; -export * from "./XMLList"; diff --git a/test/browser/TestBundler.ts b/test/browser/TestBundler.ts new file mode 100644 index 0000000..3dabf05 --- /dev/null +++ b/test/browser/TestBundler.ts @@ -0,0 +1,35 @@ +import browserify, { BrowserifyObject } from "browserify"; +import fs from "fs"; + +export namespace TestBundler { + function bundle(input: string, output: string): Promise { + return new Promise((resolve, reject) => { + const bundler: BrowserifyObject = browserify(input); + bundler.external("worker_threads"); + bundler.bundle((err, src) => { + if (err) reject(err); + else { + fs.writeFile(output, src, (err) => { + if (err) reject(err); + else resolve(); + }); + } + }); + }); + } + + export async function execute(): Promise { + const INSTANCES = [ + "worker-server", + "worker-client", + "shared-worker-server", + "shared-worker-client", + "web-client", + ]; + for (const instance of INSTANCES) + await bundle( + `${__dirname}/${instance}.js`, + `${__dirname}/../../../bundle/${instance}.js`, + ); + } +} diff --git a/test/browser/index.ts b/test/browser/index.ts new file mode 100644 index 0000000..dedcbcf --- /dev/null +++ b/test/browser/index.ts @@ -0,0 +1,62 @@ +import * as puppeteer from "puppeteer"; +import { TestBundler } from "./TestBundler"; +const HttpServer = require("local-web-server"); + +const PORT = 37792; +const ROOT = "http://127.0.0.1:" + PORT; + +function _Test_page(page: puppeteer.Page): Promise { + return new Promise((resolve, reject) => { + page.on("framenavigated", resolve); + page.on("close", resolve); + page.on("pageerror", reject); + // page.on("console", msg => + // { + // console.log(msg.text()); + // }); + }); +} + +async function _Paginate( + browser: puppeteer.Browser, + url: string, +): Promise { + console.log("\t" + url); + url = ROOT + "/" + url; + + const page = await browser.newPage(); + await page.goto(url); + await _Test_page(page); +} + +async function main(): Promise { + // DO BUNDLING + await TestBundler.execute(); + + // PREPARE SERVER & BROWSER + const server = new HttpServer().listen({ + directory: __dirname + "/../../../bundle", + port: 37792, + }); + const browser = await puppeteer.launch({ devtools: true }); + + // TEST PAGES + try { + await import(__dirname + "/web-server.js"); + await _Paginate(browser, "web.html"); + await _Paginate(browser, "worker.html"); + await _Paginate(browser, "shared-worker.html"); + } catch (exp) { + console.log("An error has occured"); + console.log(exp); + process.exit(-1); + } + + // TERMINATES + await browser.close(); + server.close(); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/browser/internal.ts b/test/browser/internal.ts new file mode 100644 index 0000000..3fc20a8 --- /dev/null +++ b/test/browser/internal.ts @@ -0,0 +1,7 @@ +export function complete(): void { + let url: string = self.location.href; + const symbol: number = url.indexOf("#"); + if (symbol !== -1) url = url.substr(0, symbol); + + self.location.href = url + "#complete"; +} diff --git a/test/browser/shared-worker-client.ts b/test/browser/shared-worker-client.ts new file mode 100644 index 0000000..6152393 --- /dev/null +++ b/test/browser/shared-worker-client.ts @@ -0,0 +1,19 @@ +import { SharedWorkerConnector } from "tgrid"; +import { ICalculator } from "../controllers/ICalculator"; +import { complete } from "./internal"; + +window.onload = async () => { + const worker: SharedWorkerConnector = new SharedWorkerConnector( + null, + null, + ); + + // TEST RE-USABILITY + for (let i: number = 0; i < 5; ++i) { + await worker.connect("shared-worker-server.js"); + + await ICalculator.main(worker.getDriver()); + await worker.close(); + } + complete(); +}; diff --git a/test/browser/shared-worker-server.ts b/test/browser/shared-worker-server.ts new file mode 100644 index 0000000..c5137ec --- /dev/null +++ b/test/browser/shared-worker-server.ts @@ -0,0 +1,16 @@ +import { SharedWorkerServer } from "tgrid"; +import { Calculator } from "../providers/Calculator"; + +/// chrome://inspect/#workers +async function main(): Promise { + const server: SharedWorkerServer = + new SharedWorkerServer(); + await server.open(async (acceptor) => { + console.log(acceptor.header); + await acceptor.accept(new Calculator()); + }); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/browser/web-client.ts b/test/browser/web-client.ts new file mode 100644 index 0000000..6767646 --- /dev/null +++ b/test/browser/web-client.ts @@ -0,0 +1,20 @@ +import { InvalidArgument } from "tstl/exception/InvalidArgument"; + +import { ICalculator } from "../controllers/ICalculator"; +import { complete } from "./internal"; +import { Driver, WebConnector } from "tgrid"; + +window.onload = async () => { + for (let i: number = 0; i < 5; ++i) { + const connector: WebConnector = new WebConnector(null, null); + await connector.connect("ws://127.0.0.1:10489"); + + const driver: Driver = connector.getDriver(); + if (driver instanceof Driver === false) + throw new InvalidArgument("Error on Driver type checking"); + + await ICalculator.main(driver); + await connector.close(); + } + complete(); +}; diff --git a/test/browser/web-server.ts b/test/browser/web-server.ts new file mode 100644 index 0000000..7ac6293 --- /dev/null +++ b/test/browser/web-server.ts @@ -0,0 +1,18 @@ +import { WebServer } from "tgrid"; +import { Calculator } from "../providers/Calculator"; + +async function main(): Promise { + const server: WebServer = new WebServer(); + let index: number = 0; + + await server.open(10489, async (acceptor) => { + await acceptor.accept(new Calculator()); + + await acceptor.join(); + if (++index === 5) await server.close(); + }); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/browser/worker-client.ts b/test/browser/worker-client.ts new file mode 100644 index 0000000..7e6fc76 --- /dev/null +++ b/test/browser/worker-client.ts @@ -0,0 +1,25 @@ +import "whatwg-fetch"; + +import { ICalculator } from "../controllers/ICalculator"; +import { complete } from "./internal"; +import { WorkerConnector } from "tgrid"; + +async function get_source(): Promise { + let url: string = location.href; + url = url.substr(0, url.lastIndexOf("/")) + "/worker-server.js"; + + const response: Response = await fetch(url, { method: "GET" }); + return await response.text(); +} + +window.onload = async () => { + const worker: WorkerConnector = new WorkerConnector(null, null); + + for (let i: number = 0; i < 5; ++i) { + await worker.compile(await get_source()); + + await ICalculator.main(worker.getDriver()); + await worker.close(); + } + complete(); +}; diff --git a/test/browser/worker-server.ts b/test/browser/worker-server.ts new file mode 100644 index 0000000..bcabf65 --- /dev/null +++ b/test/browser/worker-server.ts @@ -0,0 +1,11 @@ +import { WorkerServer } from "tgrid"; +import { Calculator } from "../providers/Calculator"; + +async function main(): Promise { + const server: WorkerServer = new WorkerServer(); + await server.open(new Calculator()); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/controllers/ICalculator.ts b/test/controllers/ICalculator.ts new file mode 100644 index 0000000..3f21a1f --- /dev/null +++ b/test/controllers/ICalculator.ts @@ -0,0 +1,92 @@ +import { DomainError } from "tstl/exception/DomainError"; +import { InvalidArgument } from "tstl/exception/InvalidArgument"; +import { randint } from "tstl/algorithm/random"; + +import { Calculator } from "../providers/Calculator"; +import { Driver } from "tgrid"; + +export interface ICalculator extends ISimple { + scientific: IScientific; + statistics: IStatistics; +} + +export interface ISimple { + 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 IScientific { + pow(x: number, y: number): number; + sqrt(x: number): number; + log(x: number, y: number): number; +} +export interface IStatistics { + mean(...elems: number[]): number; + stdev(...elems: number[]): number; +} + +export namespace ICalculator { + export async function main(driver: Driver): Promise { + // VALIDATOR + const validator: Calculator = new Calculator(); + + // CALL FUNCTIONS IN SERVER FROM CLIENT + for (let i: number = 0; i < 100; ++i) await validate(driver, validator); + + // EXCEPTION THROWN BY THE SERVER + if ((await get_exception(driver)) === null) + throw new DomainError("Throwing exception doesn't work."); + } + + async function validate( + driver: Driver, + validator: Calculator, + ): Promise { + if (driver === validator) + throw new InvalidArgument("Mistaken arguments."); + + // SPECIFY METHODS + const method: IMethod = METHODS[randint(0, METHODS.length - 1)]; + const x: number = randint(2, 10); + const y: number = randint(2, 10); + + // CALL FUNCTION & GET ANSWER + const ret: number = await method(driver, x, y); + const answer: number = method(validator, x, y) as number; + + // VALIDATE + if (ret !== answer) throw new DomainError("Error on function calling."); + } + + export async function get_exception( + driver: Driver, + ): Promise { + try { + await driver.divides(2, 0); + } catch (exp) { + return (exp as Error).message; + } + return null; + } + + const METHODS: IMethod[] = [ + (calc, x, y) => calc.plus(x, y), + (calc, x, y) => calc.minus(x, y), + (calc, x, y) => calc.multiplies(x, y), + (calc, x, y) => calc.divides(x, y), + (calc, x, y) => calc.scientific.pow(x, y), + (calc, x, y) => calc.scientific.log(x, y), + (calc, x) => calc.scientific.sqrt(x), + (calc, x, y) => calc.statistics.mean(x, y), + (calc, x, y) => calc.statistics.stdev(x, y), + ]; + + interface IMethod { + ( + calculator: Calculator | Driver, + x: number, + y: number, + ): number | Promise; + } +} diff --git a/test/controllers/IChatPrinter.ts b/test/controllers/IChatPrinter.ts new file mode 100644 index 0000000..a7639e4 --- /dev/null +++ b/test/controllers/IChatPrinter.ts @@ -0,0 +1,3 @@ +export interface IChatPrinter { + print(name: string, content: string): void; +} diff --git a/test/controllers/IChatService.ts b/test/controllers/IChatService.ts new file mode 100644 index 0000000..38e81c1 --- /dev/null +++ b/test/controllers/IChatService.ts @@ -0,0 +1,4 @@ +export interface IChatService { + setName(val: string): boolean; + talk(str: string): void; +} diff --git a/test/controllers/IScript.ts b/test/controllers/IScript.ts new file mode 100644 index 0000000..a87273e --- /dev/null +++ b/test/controllers/IScript.ts @@ -0,0 +1,50 @@ +import { DomainError } from "tstl/exception/DomainError"; +import { LengthError } from "tstl/exception/LengthError"; + +export interface IScript { + name: string; + message: string; +} + +export namespace IScript { + export const SCENARIO: IScript[] = [ + { + name: "Administrator", + message: "The Chat Application has been started.", + }, + { + name: "Jeongho Nam", + message: "Hello, my name is Jeongho Nam, author of the TGrid.", + }, + { name: "Sam", message: "I'm Sam, nice to meet you." }, + { + name: "Administrator", + message: "Welcome two participants. Nice to meet you too.", + }, + { name: "Jeongho Nam", message: "Nice to meet you three." }, + { name: "Mad Scientist", message: "HAHAHAHAHAHA" }, + { name: "Sam", message: "???????" }, + { + name: "Jeongho Nam", + message: "I'm going to sleep. See you tomorrow.", + }, + { name: "Administrator", message: "See ya~" }, + { name: "Mad Scientist", message: "Conquer all over the world~!!" }, + { name: "Sam", message: "Good bye~!!" }, + ]; + export const PEOPLE: string[] = [ + ...new Set(SCENARIO.map((script) => script.name)), + ]; + + export function validate(scripts: IScript[]): void { + if (scripts.length !== SCENARIO.length) + throw new LengthError("Different length between two scripts."); + + for (let i: number = 0; i < scripts.length; ++i) + if ( + scripts[i].name !== SCENARIO[i].name || + scripts[i].message !== SCENARIO[i].message + ) + throw new DomainError("Different script exists."); + } +} diff --git a/test/controllers/IVector.ts b/test/controllers/IVector.ts new file mode 100644 index 0000000..6da024d --- /dev/null +++ b/test/controllers/IVector.ts @@ -0,0 +1,30 @@ +import { DomainError } from "tstl/exception/DomainError"; +import { randint } from "tstl/algorithm/random"; +import { Driver } from "tgrid"; + +export interface IVector { + size(): T; + at(index: number): T; + set(index: number, val: T): void; + push_back(val: T): void; +} + +export namespace IVector { + export async function main(driver: Driver>): Promise { + let mySum: number = 0; + let serverSum: number = 0; + + for (let i: number = 0; i < 10; ++i) { + const val: number = randint(1, 10); + + mySum += val; + await driver.push_back(val); + } + + for (let i: number = 0; i < (await driver.size()); ++i) + serverSum += await driver.at(i); + + if (mySum !== serverSum) + throw new DomainError("Error on function returning."); + } +} diff --git a/test/node/components/test_pseudo.ts b/test/node/components/test_pseudo.ts new file mode 100644 index 0000000..dd8f1df --- /dev/null +++ b/test/node/components/test_pseudo.ts @@ -0,0 +1,48 @@ +import { Communicator, Invoke, Driver } from "tgrid"; +import { InvalidArgument } from "tstl/exception/InvalidArgument"; + +import { Calculator } from "../../providers/Calculator"; +import { ICalculator } from "../../controllers/ICalculator"; + +class PseudoCommunicator extends Communicator { + private sender_: (invoke: Invoke) => void; + + public constructor(sender: (invoke: Invoke) => void, provider: Provider) { + super(provider); + this.sender_ = sender; + } + + protected inspectReady(): Error | null { + return null; + } + protected async sendData(invoke: Invoke): Promise { + this.sender_(invoke); + } + public reply(invoke: Invoke): void { + this.replyData(invoke); + } +} + +export async function test_pseudo(): Promise { + //---- + // SERVER & CLIENT + //---- + // PRE-DECLARATIONS + let server: PseudoCommunicator; + let client: PseudoCommunicator; + + // CONSTRUCT SYSTEMS + server = new PseudoCommunicator((ivk) => client.reply(ivk), new Calculator()); + client = new PseudoCommunicator((ivk) => server.reply(ivk), null); + + //---- + // INTERACTS + //---- + // GET DRIVER + const driver: Driver = client.getDriver(); + if (driver instanceof Driver !== true) + throw new InvalidArgument("Error on Driver type checking"); + + // DO TEST + await ICalculator.main(driver); +} diff --git a/test/node/components/test_security.ts b/test/node/components/test_security.ts new file mode 100644 index 0000000..894a5bc --- /dev/null +++ b/test/node/components/test_security.ts @@ -0,0 +1,47 @@ +import { Driver, WebConnector, WebServer } from "tgrid"; + +import { Calculator, Scientific } from "../../providers/Calculator"; + +class CustomCalculator extends Calculator { + public _scientific = new Scientific(); + + public multiplies_(x: number, y: number): number { + return x * y; + } +} + +async function must_be_error( + procedures: (() => Promise)[], +): Promise { + let count: number = 0; + for (const proc of procedures) + try { + await proc(); + } catch (exp) { + ++count; + } + if (count !== procedures.length) throw new Error("Must be security error"); +} + +export async function test_security(): Promise { + const server: WebServer = new WebServer(); + await server.open(10101, async (acceptor) => { + await acceptor.accept(new CustomCalculator()); + }); + + const connector: WebConnector = new WebConnector(null, null); + await connector.connect("ws://127.0.0.1:10101"); + + const calc: Driver = connector.getDriver(); + await must_be_error([ + () => (calc.plus as any).toString(), + () => (calc as any).prototype.toString(), + // () => calc._scientific.log(2, 16), + // () => calc.multiplies_(4, 5), + () => (calc.scientific as any).constructor.toString(), + () => (calc.statistics.mean as any).prototype.minus(7, 4), + ]); + + await connector.close(); + await server.close(); +} diff --git a/test/node/index.ts b/test/node/index.ts new file mode 100644 index 0000000..811c52a --- /dev/null +++ b/test/node/index.ts @@ -0,0 +1,67 @@ +const EXTENSION = __filename.substr(-2); +if (EXTENSION === "js") require("source-map-support").install(); + +import fs from "fs"; + +interface IModule { + [key: string]: () => Promise; +} + +async function iterate(path: string): Promise { + const file_list: string[] = fs.readdirSync(path); + for (const file of file_list) { + const current_path: string = path + "/" + file; + const stat: fs.Stats = fs.lstatSync(current_path); + + if (stat.isDirectory() === true && file !== "internal") { + await iterate(current_path); + continue; + } else if ( + file.substr(-3) !== ".js" || + current_path === __dirname + "/index.js" + ) + continue; + + const external: IModule = await import( + current_path.substr(0, current_path.length - 3) + ); + for (const key in external) + if (key.substr(0, 5) === "test_") { + // WHEN SPECIALIZED + if (process.argv[2] && key.replace("test_", "") !== process.argv[2]) + continue; + + // DO TEST + console.log(key); + await external[key](); + } + } +} + +async function main(): Promise { + //---- + // DO TEST + //---- + const time: number = Date.now(); + await iterate(__dirname); + + //---- + // TRACE BENCHMARK + //---- + // ELAPSED TIME + console.log("----------------------------------------------------------"); + console.log("Success"); + console.log(` - elapsed time: ${(Date.now() - time).toLocaleString()} ms`); + + // MEMORY USAGE + const memory: NodeJS.MemoryUsage = process.memoryUsage(); + for (const property in memory) { + const amount: number = + memory[property as keyof NodeJS.MemoryUsage] / 10 ** 6; + console.log(` - ${property}: ${amount.toLocaleString()} MB`); + } +} +main().catch((e) => { + console.log(e); + process.exit(1); +}); diff --git a/test/node/protocols/web/test_web_calculator.ts b/test/node/protocols/web/test_web_calculator.ts new file mode 100644 index 0000000..9a1c27c --- /dev/null +++ b/test/node/protocols/web/test_web_calculator.ts @@ -0,0 +1,50 @@ +import { Calculator } from "../../../providers/Calculator"; +import { ICalculator } from "../../../controllers/ICalculator"; +import { IVector } from "../../../controllers/IVector"; + +import { Vector } from "tstl/container/Vector"; +import { Driver, WebConnector, WebServer } from "tgrid"; + +const PORT: number = 10101; + +export async function test_web_calculator(): Promise { + //---- + // SERVER + //---- + const server: WebServer> = + new WebServer(); + await server.open(PORT, async (acceptor) => { + // SPEICFY PROVIDER + const provider = /calculator/.test(acceptor.path) + ? new Calculator() + : new Vector(); + + // ALLOW CONNECTION + await acceptor.accept(provider); + }); + + //---- + // CLIENTS + //---- + const connector: WebConnector = new WebConnector(null, null); + + // const RE-USABILITY + for (const path of ["calculator", "vector"]) + for (let i: number = 0; i < 3; ++i) { + // DO CONNECT + await connector.connect(`ws://127.0.0.1:${PORT}/${path}`); + + // SET DRIVER AND TEST BY CALCULATOR PROCESS + if (path === "calculator") { + const driver: Driver = connector.getDriver(); + await ICalculator.main(driver); + } else { + const driver: Driver> = connector.getDriver(); + await IVector.main(driver); + } + await connector.close(); + } + + // CLOSE SERVER + await server.close(); +} diff --git a/test/node/protocols/web/test_web_chat.ts b/test/node/protocols/web/test_web_chat.ts new file mode 100644 index 0000000..e4ce5c4 --- /dev/null +++ b/test/node/protocols/web/test_web_chat.ts @@ -0,0 +1,106 @@ +import { Driver, WebConnector, WebServer } from "tgrid"; +import { sleep_for } from "tstl/thread/global"; + +import { IScript } from "../../../controllers/IScript"; +import { IChatPrinter } from "../../../controllers/IChatPrinter"; +import { IChatService } from "../../../controllers/IChatService"; +import { ChatService } from "../../../providers/ChatService"; + +const PORT = 10101; + +/* ---------------------------------------------------------------- +CLIENT +---------------------------------------------------------------- */ +class Client { + private name_!: string; + private connector_!: WebConnector; + private scripts_!: IScript[]; + + private service_!: Driver; + + public async participate(name: string): Promise { + // ASSIGN MEMBERS + this.name_ = name; + this.connector_ = new WebConnector(null, { + print: (name: string, message: string): void => { + this.scripts_.push({ name: name, message: message }); + }, + }); + this.service_ = this.connector_.getDriver(); + this.scripts_ = []; + + // PREPARE INTERACTION + await this.connector_.connect("http://127.0.0.1:" + PORT); + await this.service_.setName(name); + } + + public async shout(): Promise { + for (const script of IScript.SCENARIO) { + await sleep_for(50); + if (script.name === this.name_) await this.service_.talk(script.message); + } + } + + public async close(): Promise { + await this.connector_.close(); + return this.scripts_; + } +} + +/* ---------------------------------------------------------------- + SERVER +---------------------------------------------------------------- */ +class Server { + private server_!: WebServer; + private scripts_!: IScript[]; + + public async open(): Promise { + this.server_ = new WebServer(); + this.scripts_ = []; + + await this.server_.open(PORT, async (acceptor) => { + const service: ChatService = new ChatService(); + service.assign(acceptor.getDriver(), this.scripts_); + + await acceptor.accept(service); + await acceptor.join(); + + service.destroy(); + }); + } + + public async close(): Promise { + await this.server_.close(); + return this.scripts_; + } +} + +/* ---------------------------------------------------------------- + MAIN +---------------------------------------------------------------- */ +export async function test_web_chat(): Promise { + // OPEN SERVER + const server: Server = new Server(); + await server.open(); + + // PREPARE CLIENTS + const clients: Client[] = []; + for (const name of IScript.PEOPLE) { + const c: Client = new Client(); + await c.participate(name); + + clients.push(c); + } + + // START CHATTING + const promiseList: Promise[] = []; + for (const c of clients) promiseList.push(c.shout()); + await Promise.all(promiseList); + + // VALIDATIONS + for (const c of clients) { + const scripts: IScript[] = await c.close(); + IScript.validate(scripts); + } + IScript.validate(await server.close()); +} diff --git a/test/node/protocols/web/test_web_header.ts b/test/node/protocols/web/test_web_header.ts new file mode 100644 index 0000000..0870786 --- /dev/null +++ b/test/node/protocols/web/test_web_header.ts @@ -0,0 +1,25 @@ +import { WebConnector, WebServer } from "tgrid"; + +const PORT = 10101; +const TOKEN = "asdfawe4fasdfchswrtgadfg"; + +interface IHeaders { + token: string; +} + +export async function test_web_header(): Promise { + const server: WebServer = new WebServer(); + await server.open(PORT, async (acceptor) => { + if (acceptor.header.token !== TOKEN) await acceptor.reject(); + else await acceptor.accept(null); + }); + + const connector: WebConnector = new WebConnector( + { token: TOKEN }, + null, + ); + await connector.connect(`ws://127.0.0.1:${PORT}`); + await connector.close(); + + await server.close(); +} diff --git a/test/node/protocols/web/test_web_mutex.ts b/test/node/protocols/web/test_web_mutex.ts new file mode 100644 index 0000000..cfb39b1 --- /dev/null +++ b/test/node/protocols/web/test_web_mutex.ts @@ -0,0 +1,86 @@ +import { Vector } from "tstl/container/Vector"; +import { Mutex } from "tstl/thread/Mutex"; +import { DomainError } from "tstl/exception/DomainError"; +import { RuntimeError } from "tstl/exception/RuntimeError"; + +import { is_sorted } from "tstl/algorithm/sorting"; +import { sleep_for } from "tstl/thread/global"; +import { Driver, WebConnector, WebServer } from "tgrid"; + +const PORT: number = 10101; +const COUNT: number = 10; +const SLEEP_TIME: number = 10; + +class Provider { + public mutex: Mutex; + public vector: Vector; + private index_: number; + + public constructor(mutex: Mutex, vector: Vector, index: number) { + this.mutex = mutex; + this.vector = vector; + this.index_ = index; + } + + public getIndex(): number { + return this.index_; + } +} + +async function _Test_client(): Promise { + const connector: WebConnector = new WebConnector(null, null); + await connector.connect(`ws://127.0.0.1:${PORT}`); + + const driver: Driver = connector.getDriver(); + await driver.mutex.lock(); + { + const index: number = await driver.getIndex(); + + await sleep_for((COUNT - index) * SLEEP_TIME); + await driver.vector.push_back(index); + } + await driver.mutex.unlock(); + + await connector.close(); +} + +export async function test_web_mutex(): Promise { + //---- + // PREPARES + //---- + // OPEN SERVER + const server: WebServer = new WebServer(); + const mutex: Mutex = new Mutex(); + const vector: Vector = new Vector(); + let index: number = 0; + + await server.open(PORT, async (acceptor) => { + await acceptor.accept(new Provider(mutex, vector, index++)); + }); + + // PREPARE LATCH & TIME RECORDER + const promiseList: Promise[] = []; + const time: number = Date.now(); + + // CREATE CLIENTS + for (let i: number = 0; i < COUNT; ++i) promiseList.push(_Test_client()); + + // WAIT PROMISES + await Promise.all(promiseList); + + //---- + // VALIDATIONS + //---- + // MUTEX.LOCK CONSUMED PROPER TIME? + if (Date.now() - time < COUNT * ((COUNT * SLEEP_TIME) / 2.0)) + throw new RuntimeError("remote mutex lock is not exact."); + + // ELEMENTS MUST BE SORTED BY THE CRITICAL SECTION + if (is_sorted(vector.begin(), vector.end()) === false) + throw new DomainError( + "remote mutex lock does not ensure the critical section.", + ); + + // CLOSE THE SERVER + await server.close(); +} diff --git a/test/node/protocols/web/test_web_reject.ts b/test/node/protocols/web/test_web_reject.ts new file mode 100644 index 0000000..4870de4 --- /dev/null +++ b/test/node/protocols/web/test_web_reject.ts @@ -0,0 +1,26 @@ +import { WebConnector, WebServer } from "tgrid"; + +const PORT = 10101; + +export async function test_web_reject(): Promise { + const server = new WebServer(); + + // TEST RE-USABILITY TOO + for (let i: number = 0; i < 5; ++i) { + await server.open(PORT, async (acceptor) => { + await acceptor.reject(1001, "Rejected by test automation program."); + }); + + const connector: WebConnector = new WebConnector(null, null); + let error: Error | null = null; + + try { + await connector.connect(`ws://127.0.0.1:${PORT}`); + } catch (exp) { + error = exp as Error; + } + await server.close(); + + if (error === null) throw new Error("Catching reject has failed."); + } +} diff --git a/test/node/protocols/web/test_web_server_close.ts b/test/node/protocols/web/test_web_server_close.ts new file mode 100644 index 0000000..7a8a0ad --- /dev/null +++ b/test/node/protocols/web/test_web_server_close.ts @@ -0,0 +1,14 @@ +import { WebConnector, WebServer } from "tgrid"; + +const PORT = 10101; + +export async function test_web_server_close(): Promise { + const server: WebServer = new WebServer(); + await server.open(PORT, (acceptor) => acceptor.accept(null)); + + for (let i: number = 0; i < 100; ++i) { + const connector: WebConnector = new WebConnector(null, null); + await connector.connect(`ws://127.0.0.1:${PORT}`); + } + await server.close(); +} diff --git a/test/node/protocols/workers/internal/calculator.ts b/test/node/protocols/workers/internal/calculator.ts new file mode 100644 index 0000000..6eeca95 --- /dev/null +++ b/test/node/protocols/workers/internal/calculator.ts @@ -0,0 +1,38 @@ +import { Simple } from "../../../../providers/Calculator"; +import { IScientific, IStatistics } from "../../../../controllers/ICalculator"; +import { Driver, WorkerConnector, WorkerServer } from "tgrid"; + +class HierarchicalCalculator extends Simple { + // REMOTE CALCULATOR + public scientific!: Driver; + public statistics!: Driver; +} + +async function get( + path: string, +): Promise> { + // DO CONNECT + const connector: WorkerConnector = new WorkerConnector( + null, + null, + ); + await connector.connect(path); + + // RETURN DRIVER + return connector.getDriver(); +} + +async function main(): Promise { + // PREPARE REMOTE CALCULATOR + const calc = new HierarchicalCalculator(); + calc.scientific = await get(__dirname + "/scientific.js"); + calc.statistics = await get(__dirname + "/statistics.js"); + + // OPEN SERVER + const server = new WorkerServer(); + await server.open(calc); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/node/protocols/workers/internal/chat-child.ts b/test/node/protocols/workers/internal/chat-child.ts new file mode 100644 index 0000000..881eca1 --- /dev/null +++ b/test/node/protocols/workers/internal/chat-child.ts @@ -0,0 +1,38 @@ +import { IScript } from "../../../../controllers/IScript"; +import { IChatService } from "../../../../controllers/IChatService"; + +import { sleep_for } from "tstl/thread/global"; +import { Driver, WorkerServer } from "tgrid"; + +async function main(): Promise { + //---- + // PREPARATIONS + //---- + // OPEN SERVER + const server: WorkerServer<{ name: string }, object> = new WorkerServer(); + const scripts: IScript[] = []; + + // PREPARE ASSETS + const myName: string = (await server.getHeader()).name; + const service: Driver = server.getDriver(); + + await server.open({ + shout: async () => { + for (const script of IScript.SCENARIO) { + await sleep_for(50); + if (script.name === myName) await service.talk(script.message); + } + }, + print: (name: string, message: string) => { + scripts.push({ name: name, message: message }); + }, + validate: () => { + IScript.validate(scripts); + }, + }); + await service.setName(myName); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/node/protocols/workers/internal/join.ts b/test/node/protocols/workers/internal/join.ts new file mode 100644 index 0000000..db2095c --- /dev/null +++ b/test/node/protocols/workers/internal/join.ts @@ -0,0 +1,16 @@ +import fs from "fs"; +import { WorkerServer } from "tgrid"; + +const FILE_PATH = __dirname + "/../log.dat"; + +async function main(): Promise { + const server: WorkerServer = new WorkerServer(); + await server.open(null); + await server.join(); + + await fs.promises.writeFile(FILE_PATH, "WorkerServer.join()", "utf8"); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/node/protocols/workers/internal/scientific.ts b/test/node/protocols/workers/internal/scientific.ts new file mode 100644 index 0000000..d1fb87d --- /dev/null +++ b/test/node/protocols/workers/internal/scientific.ts @@ -0,0 +1,11 @@ +import { WorkerServer } from "tgrid"; +import { Scientific } from "../../../../providers/Calculator"; + +async function main(): Promise { + const server: WorkerServer = new WorkerServer(); + await server.open(new Scientific()); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/node/protocols/workers/internal/statistics.ts b/test/node/protocols/workers/internal/statistics.ts new file mode 100644 index 0000000..24823e4 --- /dev/null +++ b/test/node/protocols/workers/internal/statistics.ts @@ -0,0 +1,11 @@ +import { WorkerServer } from "tgrid"; +import { Statistics } from "../../../../providers/Calculator"; + +async function main(): Promise { + const server: WorkerServer = new WorkerServer(); + await server.open(new Statistics()); +} +main().catch((exp) => { + console.log(exp); + process.exit(-1); +}); diff --git a/test/node/protocols/workers/test_hierarchical_workers.ts b/test/node/protocols/workers/test_hierarchical_workers.ts new file mode 100644 index 0000000..b22c8b1 --- /dev/null +++ b/test/node/protocols/workers/test_hierarchical_workers.ts @@ -0,0 +1,20 @@ +import { Driver, WorkerConnector } from "tgrid"; +import { ICalculator } from "../../../controllers/ICalculator"; + +export async function test_hierarchical_workers(): Promise { + const connector: WorkerConnector = new WorkerConnector( + null, + null, + ); + for (let i: number = 0; i < 5; ++i) { + // DO CONNECT + await connector.connect(__dirname + "/internal/calculator.js"); + + // DO TEST + const driver: Driver = connector.getDriver(); + await ICalculator.main(driver); + + // TERMINATE + await connector.close(); + } +} diff --git a/test/node/protocols/workers/test_worker.ts b/test/node/protocols/workers/test_worker.ts new file mode 100644 index 0000000..0a6eea2 --- /dev/null +++ b/test/node/protocols/workers/test_worker.ts @@ -0,0 +1,11 @@ +import { WorkerConnector } from "tgrid"; +import { IScientific } from "../../../controllers/ICalculator"; + +export async function test_worker(): Promise { + const worker: WorkerConnector = new WorkerConnector(null, null); + await worker.connect(__dirname + "/internal/scientific.js"); + + if ((await worker.getDriver().pow(2, 4)) !== Math.pow(2, 4)) + throw new Error("Unknown error on worker"); + await worker.close(); +} diff --git a/test/node/protocols/workers/test_worker_chat.ts b/test/node/protocols/workers/test_worker_chat.ts new file mode 100644 index 0000000..938dba1 --- /dev/null +++ b/test/node/protocols/workers/test_worker_chat.ts @@ -0,0 +1,52 @@ +import { DomainError } from "tstl/exception/DomainError"; +import { LengthError } from "tstl/exception/LengthError"; + +export interface IScript { + name: string; + message: string; +} + +export namespace IScript { + export const SCENARIO: IScript[] = [ + { + name: "Administrator", + message: "The Chat Application has been started.", + }, + { + name: "Jeongho Nam", + message: "Hello, my name is Jeongho Nam, author of the TGrid.", + }, + { name: "Sam", message: "I'm Sam, nice to meet you." }, + { + name: "Administrator", + message: "Welcome two participants. Nice to meet you too.", + }, + { name: "Jeongho Nam", message: "Nice to meet you three." }, + { name: "Mad Scientist", message: "HAHAHAHAHAHA" }, + { name: "Sam", message: "???????" }, + { + name: "Jeongho Nam", + message: "I'm going to sleep. See you tomorrow.", + }, + { name: "Administrator", message: "See ya~" }, + { name: "Mad Scientist", message: "Conquer all over the world~!!" }, + { name: "Sam", message: "Good bye~!!" }, + ]; + export const PEOPLE: string[] = [ + ...new Set(SCENARIO.map((script) => script.name)), + ]; + + export function validate(scriptList: IScript[]): void { + // VALIDATE LENGTH + if (scriptList.length !== SCENARIO.length) + throw new LengthError("Different length between two scripts."); + + // VALIDATE MESSAGES + for (const script of scriptList) { + const index: number = SCENARIO.findIndex((s) => { + return s.name === script.name && s.message === script.message; + }); + if (index === -1) throw new DomainError("Different script exists."); + } + } +} diff --git a/test/node/protocols/workers/test_worker_compiler.ts b/test/node/protocols/workers/test_worker_compiler.ts new file mode 100644 index 0000000..d42a4b2 --- /dev/null +++ b/test/node/protocols/workers/test_worker_compiler.ts @@ -0,0 +1,41 @@ +import fs from "fs"; +import cp from "child_process"; + +import { ICalculator } from "../../../controllers/ICalculator"; +import { WorkerConnector } from "tgrid"; + +export function test_worker_connect(): Promise { + return _Test_worker( + (worker) => + worker.connect(__dirname + "/../../../browser/worker-server.js"), + "process", + ); +} + +export async function test_worker_compile(): Promise { + const PATH = __dirname + "/../../../../../bundle/worker-server.js"; + if (fs.existsSync(PATH) === false) cp.execSync("npm run bundle"); + + await _Test_worker( + (worker) => worker.compile(fs.readFileSync(PATH, "utf8")), + "thread", + ); +} + +async function _Test_worker( + connect: (obj: WorkerConnector) => Promise, + type: "thread" | "process", +): Promise { + const worker: WorkerConnector = new WorkerConnector( + null, + null, + type, + ); + + // TEST RE-USABILITY + for (let i: number = 0; i < 5; ++i) { + await connect(worker); + await ICalculator.main(worker.getDriver()); + await worker.close(); + } +} diff --git a/test/node/protocols/workers/test_worker_join.ts b/test/node/protocols/workers/test_worker_join.ts new file mode 100644 index 0000000..db15f99 --- /dev/null +++ b/test/node/protocols/workers/test_worker_join.ts @@ -0,0 +1,26 @@ +import { WorkerConnector } from "tgrid"; +import { FileSystem } from "tgrid/lib/protocols/workers/internal/FileSystem"; +import { sleep_for } from "tstl/thread"; + +const FILE_PATH = __dirname + "/log.dat"; + +export async function test_worker_join(): Promise { + await FileSystem.write(FILE_PATH, "NOT YET"); + + const connector: WorkerConnector = new WorkerConnector( + null, + null, + ); + await connector.connect(__dirname + "/internal/join.js"); + + sleep_for(100) + .then(() => connector.close()) + .catch(() => {}); + await connector.join(); + + const content: string = await FileSystem.read(FILE_PATH, "utf8"); + await FileSystem.unlink(FILE_PATH); + + if (content !== "WorkerServer.join()") + throw new Error("Error on WorkerServer.join()"); +} diff --git a/test/providers/Calculator.ts b/test/providers/Calculator.ts new file mode 100644 index 0000000..24e08c9 --- /dev/null +++ b/test/providers/Calculator.ts @@ -0,0 +1,61 @@ +import { + ICalculator, + ISimple, + IScientific, + IStatistics, +} from "../controllers/ICalculator"; + +export class Simple implements ISimple { + public plus(x: number, y: number): number { + return x + y; + } + public minus(x: number, y: number): number { + return x - y; + } + + public multiplies(x: number, y: number): number { + return x * y; + } + public divides(x: number, y: number): number { + if (y === 0) throw new Error("Divided by zero."); + return x / y; + } +} + +export class Calculator extends Simple implements ICalculator { + public scientific = new Scientific(); + public statistics = new Statistics(); +} + +export class Scientific implements IScientific { + public pow(x: number, y: number): number { + return Math.pow(x, y); + } + + public log(x: number, y: number): number { + if (x < 0 || y < 0) throw new Error("Negative value on log."); + return Math.log(y) / Math.log(x); + } + + public sqrt(x: number): number { + if (x < 0) throw new Error("Negative value on sqaure."); + return Math.sqrt(x); + } +} + +export class Statistics implements IStatistics { + public mean(...elems: number[]): number { + let ret: number = 0; + for (const val of elems) ret += val; + return ret / elems.length; + } + + public stdev(...elems: number[]): number { + const mean: number = this.mean(...elems); + let ret: number = 0; + + for (const val of elems) ret += Math.pow(val - mean, 2); + + return Math.sqrt(ret / elems.length); + } +} diff --git a/test/providers/ChatService.ts b/test/providers/ChatService.ts new file mode 100644 index 0000000..aa53187 --- /dev/null +++ b/test/providers/ChatService.ts @@ -0,0 +1,55 @@ +import { IChatService } from "../controllers/IChatService"; + +import { HashMap } from "tstl/container/HashMap"; +import { IChatPrinter } from "../controllers/IChatPrinter"; + +import { IScript } from "../controllers/IScript"; +import { DomainError } from "tstl/exception/DomainError"; +import { Driver } from "tgrid"; + +export class ChatService implements IChatService { + private static participants_: HashMap> = + new HashMap(); + private scripts_!: IScript[]; + private driver_!: Driver; + + private name_?: string; + + public assign(driver: Driver, scripts: IScript[]): void { + this.driver_ = driver; + this.scripts_ = scripts; + } + + public destroy(): void { + if (this.name_) ChatService.participants_.erase(this.name_); + } + + public setName(str: string): boolean { + if (ChatService.participants_.has(str)) return false; // DUPLICATED NAME + + // SET NAME AND ENROLL IT TO DICTIONARY + this.name_ = str; + ChatService.participants_.emplace(str, this.driver_); + + // INFORMS TRUE TO CLIENT + return true; + } + + public talk(content: string): void { + if (!this.name_) throw new DomainError("No name"); + + // INFORM TO EVERYBODY + for (const it of ChatService.participants_) { + const driver: Driver = it.second; + + // INFORM IT TO CLIENT + const promise: Promise = driver.print(this.name_, content); + + // DISCONNECTION WHILE TALKING MAY POSSIBLE + promise.catch(() => {}); + } + + // LOG ARCHIVE + this.scripts_.push({ name: this.name_, message: content }); + } +} diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..bce977e --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../bin", + "paths": { + "tgrid": ["../src"], + "tgrid/lib/*": ["../src/*"], + } + }, + "include": [ + "./", + "../src", + ], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 00dc7e2..d956f38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,69 +1,66 @@ { - "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["DOM", "ES2015"], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - "stripInternal": true, - "skipLibCheck": true, - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - }, - "include": ["src"] + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "lib": ["DOM", "ES2015"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "stripInternal": true, + "skipLibCheck": true, + /* Advanced Options */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "plugins": [ + { "transform": "typescript-transform-paths" }, + ], + }, + "include": ["src"] }